2022秋招算法岗面经题:训练模型时loss除以10和学习率除以10真的等价吗(SGD等价,Adam不等价)

问题描述:训练深度学习模型时loss除以10和学习率除以10等价吗?

先说结论

这个问题的答案与优化器有关

  1. 使用Adam、Adagrad、RMSprop等带有二阶动量 v t v_t vt的优化器训练时,当我们将loss除以10,对训练几乎没有影响。
  2. 使用SGD、Momentum SGD等,将loss除以10,会对训练有影响,并且这个影响和学习率除以10是等价的。

具体分析

2022秋招算法岗面经题:训练模型时loss除以10和学习率除以10真的等价吗(SGD等价,Adam不等价)_第1张图片

Adam
当使用Adam优化器时,若将loss扩大 s s s倍,则梯度 g t g_t gt扩大 s s s倍,由于 m t m_t mt g t g_t gt的累加, v t v_t vt g t 2 g_t^2 gt2的累加,且 m ^ t \hat m_t m^t m t m_t mt是线性关系, v ^ t \hat v_t v^t v t v_t vt的线性关系,所以Adam的更新公式变成如下形式:
θ t = θ t − 1 − α s ∗ m ^ t s 2 ∗ v ^ t + ϵ = θ t − 1 − α m ^ t v ^ t + ϵ / s ≈ θ t − 1 − α m ^ t v ^ t + ϵ \theta_t=\theta_{t-1}-\alpha \frac{s * \hat{m}_t}{\sqrt{s^2 * \hat{v}_t}+\epsilon}=\theta_{t-1}-\alpha \frac{\hat{m}_t}{\sqrt{\hat{v}_t}+\epsilon / s}\approx \theta_{t-1}-\alpha \frac{\hat{m}_t}{\sqrt{\hat{v}_t}+\epsilon} θt=θt1αs2v^t +ϵsm^t=θt1αv^t +ϵ/sm^tθt1αv^t +ϵm^t
ϵ / s \epsilon/s ϵ/s相对于 v ^ t \sqrt{\hat{v}_t} v^t 是一个很小的量时,上面的约等于就相当于等号了。


现在我们可以尝试回答下面的问题了

问题:“在使用adam的情况下,如果人为把loss 扩大10倍,对adam来讲,会有什么影响?可以通过调整学习率来缓解这个影响吗?”

  • 对于Adam而言,如果人为把loss扩大10倍,对参数梯度更新没有什么影响;
  • Adam学习率乘上10000,对于参数更新影响巨大。

你如果不相信这个结果,那我们可以写个代码来验证下:

import torch
import torch.nn as nn
torch.manual_seed(20)

class FCModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.linear = nn.Sequential(
            nn.Linear(6, 6),
            nn.ReLU(),
            nn.Linear(6, 1),
            nn.Sigmoid())
    
    def forward(self, x):
        return self.linear(x ** 2).exp()

# reload: ensure same initialization
fc = FCModel()
torch.save(fc, 'fc.pth.tar')
fc1 = torch.load('fc.pth.tar')
fc2 = torch.load('fc.pth.tar')

# optimizer1 = torch.optim.SGD(fc1.parameters(), lr=0.01 * 10000)
# optimizer2 = torch.optim.SGD(fc2.parameters(), lr=0.01)

optimizer1 = torch.optim.Adam(fc1.parameters(), lr=0.01)
optimizer2 = torch.optim.Adam(fc2.parameters(), lr=0.01)
# optimizer2 = torch.optim.Adam(fc2.parameters(), lr=0.01 * 10000)

for i in range(20):
    input1 = torch.rand(1, 6)
    label1 = torch.rand(1,)
    output1 = fc1(input1)
    loss1 = label1 - output1
    optimizer1.zero_grad()
    loss1.backward()
    optimizer1.step()

    input2 = input1.clone()
    label2 = label1.clone()
    output2 = fc2(input2)
    loss2 = (label2 - output2) * 10000
    optimizer2.zero_grad()
    loss2.backward()
    optimizer2.step()

    print("fc1: {}".format(i), fc1.linear[0].weight)
    print("fc2: {}".format(i), fc2.linear[0].weight)
    print()

2022秋招算法岗面经题:训练模型时loss除以10和学习率除以10真的等价吗(SGD等价,Adam不等价)_第2张图片

思考题:多任务学习为什么可以 a*loss1 + b*loss2来平衡不同任务?

说到这里我有个疑问:既然对于Adam优化器,我loss除以10对训练情况几乎没有影响,那为什么在多任务学习中常用a*loss1 + b*loss2这种方式来平衡不同任务的学习呢?

后来我想了想,a*loss1确实可以让梯度g1扩大a倍,b*loss2也确实可以让梯度g2扩大b倍,这没有错,但是在Adam里面把这两个梯度加在一起进行平方的,当a和b不相同时,就没有办法提取出相同的倍数,然后分子分母消掉了,因此a*loss1 + b*loss2这种方式确实可以权衡不同任务训练时的重要性。

具体分析如下:2022秋招算法岗面经题:训练模型时loss除以10和学习率除以10真的等价吗(SGD等价,Adam不等价)_第3张图片

我们继续写个代码来验证下结论:

import torch
import torch.nn as nn

torch.manual_seed(20)


class FCModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.linear = nn.Sequential(
            nn.Linear(6, 6),
            nn.ReLU(),
            nn.Linear(6, 1),
            nn.Sigmoid())

    def forward(self, x):
        return self.linear(x ** 2).exp()


# reload: ensure same initialization
fc = FCModel()
torch.save(fc, 'fc.pth.tar')
fc1 = torch.load('fc.pth.tar')
fc2 = torch.load('fc.pth.tar')

# optimizer1 = torch.optim.SGD(fc1.parameters(), lr=0.01 * 10000)
# optimizer2 = torch.optim.SGD(fc2.parameters(), lr=0.01)

optimizer1 = torch.optim.Adam(fc1.parameters(), lr=0.01)
optimizer2 = torch.optim.Adam(fc2.parameters(), lr=0.01)
# optimizer2 = torch.optim.Adam(fc2.parameters(), lr=0.01 * 10000)

for i in range(20):
    input1 = torch.rand(1, 6)
    label1 = torch.rand(1, )
    output1 = fc1(input1)
    loss11 = label1 - output1
    loss12 = label1 + output1
    loss13 = loss11 + loss12
    optimizer1.zero_grad()
    loss13.backward()
    optimizer1.step()

    input2 = input1.clone()
    label2 = label1.clone()
    output2 = fc2(input2)
    loss21 = label2 - output2
    loss22 = label2 + output2
    loss23 = loss21 + 100 * loss22
    optimizer2.zero_grad()
    loss23.backward()
    optimizer2.step()

    print("fc1: {}".format(i), fc1.linear[0].weight)
    print("fc2: {}".format(i), fc2.linear[0].weight)
    print()

可以看到两者的参数更新情况是不一样的
2022秋招算法岗面经题:训练模型时loss除以10和学习率除以10真的等价吗(SGD等价,Adam不等价)_第4张图片

相关资料

  • 知乎:https://www.zhihu.com/question/320377013
  • 面试题:2022年互联网秋招算法岗,被虐经历

你可能感兴趣的:(知识点理解,Deep,Learning,深度学习,算法,面试)