问题描述:训练深度学习模型时loss除以10和学习率除以10等价吗?
这个问题的答案与优化器有关
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=θt−1−αs2∗v^t+ϵs∗m^t=θt−1−αv^t+ϵ/sm^t≈θt−1−α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()
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
这种方式确实可以权衡不同任务训练时的重要性。
我们继续写个代码来验证下结论:
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()