深度学习(六)损失函数和优化器
思考一下,神经网络是靠什么来判断得到的结果是好还是坏呢?
答案是通过比较神经网络自己预测的输出与真实标签的差距,也就是Loss函数。从数学上推导loss函数可以看这里。
为了找到最小的loss,通常采用的是梯度下降(Gradient Descent)的方法。
记loss为 , M 是样本的数量。
一、梯度下降 Gradient Descent
梯度下降的思想是计算loss的梯度, 并取当前点的梯度值的负数,由梯度的定义可以直到,负的梯度指向区域最小值,如果是正值就是区域最大值。步长由学习率决定,k+1次的权重值就是第k次的权重减去学习率乘梯度:
1. 批梯度下降 Batch gradient descent
上面的公式使用了数据集里的所有的样本来计算梯度,这种方法被称为批梯度下降(Batch gradient descent)。
如果我们每次迭代只计算一次梯度,那整体梯度下降的就太慢了,并且可能因为数据量太大,内存放不下。不过对于凸误差面,批梯度下降可以保证收敛到全局最小值,对于非凸面,可以保证收敛到局部最小值。
2. 随机梯度下降 Stochastic gradient descent
随机梯度下降每次只用一个样本更新参数,因此梯度下降的非常快,但不是很稳定。SGD不会像批梯度下降一样向最优点逼近,可能会在最优点附近震荡。不过也有人说如果逐渐降低学习率,SGD也会想BGD一样收敛到全局最小值或者局部最小值。
3. Mini-batch gradient descent
Mini-batch gradient descent是每次用B个样本来更新参数,B=1的时候是SGD,B=M的时候就是BGD。这样做可以得到一个更加稳定的梯度下降,同时也减少了需要计算的参数,提高了优化效率。一般来说B会取2的幂,比如2,4,8,16,32,64,128等,有利于GPU的加速。
Mini-batch gradient descent 是现在最常用的方法,通常说的SGD指的也就是Mini-batch gradient descent。SGD 的缺点是选择合适的学习率可能很困难。学习率太小会导致收敛缓慢,而学习率太大会阻碍收敛,并导致损失函数在最小值附近波动甚至发散。
二、 动量 Gradient Descent With Momentum
1. 动量 Momentum
上面提到了SGD的一个缺点,就是在每个更新参数的时候,不一定是朝着梯度下降的方向,比如下面左边的图,我们更希望在水平方向上下降的快一点,而在垂直方向上的变化量小一点。Momentum的作用就是在相关方向上加速SGD并且抑制震荡。
如果我们增加一个变量来储存上一步的更新向量:
通常取
接下来详细讲一下momentum的作用:
梯度向量
从图上可以看到蓝色的箭头是不加动量时参数要指向的方向, 红色箭头是上一次的梯度下降指向的方向,如果是区域最小值在水平方向的右边,那么绿色箭头会比蓝色箭头更快到达区域最小值。
Momentum的优点在于可以抑制震荡,加速收敛,缺点是需要调整学习率。
2. Nesterov Accelerated Gradient
Nesterov Accelerated Gradient 在momentum上做了一些改变, 计算了下一次参数的loss,计算了梯度的二阶梯度,所以比SGD+momentum收敛地更快[5]。
这块数学没怎么懂,总之它收敛比momentum更快。
3. AdaGrad
上面的优化方法都用的是固定的学习率,缺点就是需要自己调参数找最适合的学习率,所以我们要想办法,让学习率在优化过程中一起优化了。
AdaGrad 全称Adpative Gradient,方法如下, 这里的代表的是矩阵元素间的乘法:
可以看到与SGD的更新规则相比,上面公式里与学习率相同作用的,是一个基于梯度和上一次迭代里学习率的值。
Adagrad的主要好处之一是,它无需手动调整学习速度。 大多数实现使用默认值0.01并将其保留为默认值。缺点就是学习率下降的太快了。
4. RMSprop
通常取
RMSprop的优点是不会让学习率单调变小。
5. Adam
Adaptive Moment Estimation
通常
Adam 看起来是RMSprop和 momentum的结合版, 一般推荐使用Adam来训练。
6. AMSGrad
Adam 有时候被观察到不能收敛到最优解,于是就有人提出了解决方案:
是不是比Adam更好还不确定。
小结
最常用的两种优化方法是SGD+momentum 和Adam,两种方法各有优缺点:
- SGD + Nesterov momentum + 学习率衰减
优点: 可以稳定的收敛,现在依旧被许多paper使用
缺点:需要调整学习率衰减 - Adam
优点:自适应的学习率
缺点:loss曲线很难解释
三、Pytorch实现
pytorch实现SGD和Adam
import torch.optim as optim
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
# optimizer = optim.Adam(model.parameters(), lr=learning_rate)
for input, target in dataset:
# set zero gradient
optimizer.zero_grad()
output = model(input)
loss = loss_fn(output, target)
loss.backward()
# take optimization step
optimizer.step()
如果想只优化特定的一些参数:
params_to_update = []
for name, param in self.model.named_parameters():
if param.requires_grad == True:
params_to_update.append(param)
为不同参数设置不同的学习率
optimizer = torch.optim.SGD([
{'params': model.parameters()},
{'params': model.s, 'lr':1e-3}
], lr=learning_rate)
为不同的参数设置不同的优化器:
class MultipleOptimizer(object):
def __init__(self, *op):
self.optimizers = op
def zero_grad(self):
for op in self.optimizers:
op.zero_grad()
def step(self):
for op in self.optimizers:
op.step()
optimizer = MultipleOptimizer(optim.SGD([model.s], lr=0.01),
optim.Adam(model.parameters(), lr=learning_rate))
optimizer.zero_grad()
loss.backward()
optimizer.step()
如果想要根据训练的表现调节学习率,可以使用torch.optim.lr_scheduler里的函数,可以根据各种条件调节学习率的变化。