深度学习优化器

优化器

深度学习算法在许多情况下都涉及优化,我们经常使用解析优化去证明或设计算法。在深度学习的诸多优化问题中,最难的是神经网络的设计,这其中的优化问题非常重要,代价也很高,因此研究者们开发了一组专门为此设计的优化技术,也就是我们本文中要介绍的——神经网络优化器。

这些优化器主要关注一类特定的优化问题:寻找神经网络上的一组参数 θ \theta θ,它能显著地降低代价函数 J ( θ ) J(\theta) J(θ),该代价函数通常包括整个训练集上的性能评估和额外的正则化项。

神经网络优化中的挑战

  • 病态
    在优化凸函数时,会遇到一些挑战。 这其中最突出的是Hessian矩阵的病态。
    病态问题一般被认为存在于神经网络训练过程中,病态体现在随机梯度下降会卡在某些情况,此时即使很小的更新步长也会增加代价函数。
  • 局部极小值
    如果一个足够大的训练集可以唯一确定一组模型参数,那么该模型被称为辨识性的。由于模型可辨识性问题,神经网络和任意具有多个等效参数化潜变量的模型都会具有多个局部极小值。 带有潜变量的模型通常是不可辨认的,因为通过相互交换潜变量我们能得到等价的模型。
  • 高原、鞍点和其他平坦区域
    对于很多高维非凸函数而言,局部极小值(以及极大值)事实上都远少于另一类梯度为零的点:鞍点。 鞍点附近的某些点比鞍点有更大的代价,而其他点则有更小的代价。 在鞍点处,Hessian矩阵同时具有正负特征值。 位于正特征值对应的特征向量方向的点比鞍点有更大的代价,反之,位于负特征值对应的特征向量方向的点有更小的代价。 我们可以将鞍点视为代价函数某个横截面上的局部极小点,同时也可以视为代价函数某个横截面上的局部极大点。
  • 悬崖和梯度爆炸
    多层神经网络通常存在像悬崖一样的斜率较大区域,如下图所示。
    在这里插入图片描述
  • 长期依赖
    当计算图变得极深时,神经网络优化算法会面临的另外一个难题就是长期依赖问题——由于变深的结构使模型丧失了学习到先前信息的能力,让优化变得极其困难。 深层的计算图不仅存在于前馈网络,还存在于之后介绍的循环网络中。 因为循环网络要在很长时间序列的各个时刻重复应用相同操作来构建非常深的计算图,并且模型参数共享,这使问题更加凸显。
  • 非精确梯度
    大多数优化算法的先决条件都是我们知道精确的梯度或是Hessian矩阵。 在实践中,通常这些量会有噪声,甚至是有偏的估计。几乎每一个深度学习算法都需要基于采样的估计,至少使用训练样本的小批量来计算梯度。
    在其他情况,我们希望最小化的目标函数实际上是难以处理的。 当目标函数不可解时,通常其梯度也是难以处理的。
  • 局部和全局结构间的弱对应
  • 优化的理论限制

优化器种类

  • SGD: 随机梯度下降,每次迭代只训练一个样本,不能利用 CPU 或 GPU 并行计算speed up,且每个样本都进行gradient descent,这无疑增加了样本中的概率,所以学习速度较慢。
torch.optim.SGD(params, lr=, momentum=0, dampening=0, weight_decay=0, nesterov=False)
  • ASGD:平均随机梯度下降,ASGD 就是用空间换时间的一种 SGD。
torch.optim.ASGD(params, lr=0.01, lambd=0.0001, alpha=0.75, t0=1000000.0, weight_decay=0)
  • Adagrad:AdaGrad算法就是将每一个参数的每一次迭代的梯度取平方累加后在开方,用全局学习率除以这个数,作为学习率的动态更新, 是一种自适应优化方法,自适应的为各个参数分配不同的学习率。一般来说AdaGrad算法一开始是激励收敛,到了后面就慢慢变成惩罚收敛,速度越来越慢。
torch.optim.Adagrad(params, lr=0.01, lr_decay=0, weight_decay=0)
  • Adadelta:Adadelta是对Adagrad的扩展,但是进行了计算上的简化。Adagrad会累加之前所有的梯度平方,而Adadelta只累加固定大小的项,并且也不直接存储这些项,仅仅是近似计算对应的平均值。
torch.optim.Adadelta(params, lr=1.0, rho=0.9, eps=1e-06, weight_decay=0)
  • RMSprop:RMSprop 和 Adadelta 一样,也是对 Adagrad 的一种改进。 RMSprop 用均方根作为分母,可缓解 Adagrad 学习率下降较快的问题, 并且引入均方根,可以减少摆动。
torch.optim.RMSprop(params, lr=0.01, alpha=0.99, eps=1e-08, weight_decay=0, momentum=0, centered=False)
  • Adam:自适应矩估计,本质上是带有动量项的RMSprop,利用梯度的一阶矩估计和二阶矩估计动态调整每个参数的学习率。Adam的优点主要在于通过偏置校正后,每一次迭代学习率都有个确定范围,使得参数比较平稳。
torch.optim.Adam(params, lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0)
  • Adamax:Adamd的无穷范数变种,对 Adam 增加了一个学习率上限的概念,所以称之为 Adamax。
torch.optim.Adamax(params, lr=0.002, betas=(0.9, 0.999), eps=1e-08, weight_decay=0)
  • SparseAdam:针对稀疏张量的一种“阉割版”Adam 优化方法。
torch.optim.SparseAdam(params, lr=0.001, betas=(0.9, 0.999), eps=1e-08)
  • L-BFGS:L-BFGS 属于拟牛顿算法,是对 BFGS 的改进,特点就是节省内存。
torch.optim.LBFGS(params, lr=1, max_iter=20, max_eval=None, tolerance_grad=1e-05, tolerance_change=1e-09, history_size=100, line_search_fn=None)
  • Rprop:弹性反向传播算法,该优化方法适用于 full-batch,不适用于 mini-batch。
torch.optim.Rprop(params, lr=0.01, etas=(0.5, 1.2), step_sizes=(1e-06, 50))

优化器的选择

如果数据是稀疏的,就用自适用方法,即 Adagrad, Adadelta, RMSprop, Adam。

RMSprop, Adadelta, Adam 在很多情况下的效果是相似的。

Adam 就是在 RMSprop 的基础上加了 bias-correction 和 momentum,

随着梯度变的稀疏,Adam 比 RMSprop 效果会好。

整体来讲,Adam 是最好的选择。

很多论文里都会用 SGD,没有 momentum 等。SGD 虽然能达到极小值,但是比其它算法用的时间长,而且可能会被困在鞍点。

如果需要更快的收敛,或者是训练更深更复杂的神经网络,需要用一种自适应的算法。

SGD、Momentum、RMSprop 、Adam四种优化器的比较

import torch
import torch.utils.data as Data
import torch.nn.functional as F
from torch.autograd import Variable
import matplotlib.pyplot as plt
 
# 超参数
LR = 0.01
BATCH_SIZE = 32
EPOCH = 12
 
# 生成假数据
# torch.unsqueeze() 的作用是将一维变二维,torch只能处理二维的数据
x = torch.unsqueeze(torch.linspace(-1, 1, 1000), dim=1)  # x data (tensor), shape(100, 1)
# 0.2 * torch.rand(x.size())增加噪点
y = x.pow(2) + 0.1 * torch.normal(torch.zeros(*x.size()))
 
# 定义数据库
dataset = Data.TensorDataset(data_tensor = x, target_tensor = y)

# 定义数据加载器
loader = Data.DataLoader(dataset = dataset, batch_size = BATCH_SIZE, shuffle = True, num_workers = 2)

# 定义pytorch网络
class Net(torch.nn.Module):
    def __init__(self, n_features, n_hidden, n_output):
        super(Net, self).__init__()
        self.hidden = torch.nn.Linear(n_features, n_hidden)
        self.predict = torch.nn.Linear(n_hidden, n_output)
    def forward(self, x):
        x = F.relu(self.hidden(x))
        y = self.predict(x)
        return y

# 定义不同的优化器网络
net_SGD = Net(1, 10, 1)
net_Momentum = Net(1, 10, 1)
net_RMSprop = Net(1, 10, 1)
net_Adam = Net(1, 10, 1)

# 选择不同的优化方法
opt_SGD = torch.optim.SGD(net_SGD.parameters(), lr = LR)
opt_Momentum = torch.optim.SGD(net_Momentum.parameters(), lr = LR, momentum = 0.9)
opt_RMSprop = torch.optim.RMSprop(net_RMSprop.parameters(), lr = LR, alpha = 0.9)
opt_Adam = torch.optim.Adam(net_Adam.parameters(), lr = LR, betas= (0.9, 0.99))

nets = [net_SGD, net_Momentum, net_RMSprop, net_Adam]
optimizers = [opt_SGD, opt_Momentum, opt_RMSprop, opt_Adam]

# 选择损失函数
loss_func = torch.nn.MSELoss()

# 不同方法的loss
loss_SGD = []
loss_Momentum = []
loss_RMSprop =[]
loss_Adam = []

# 保存所有loss
losses = [loss_SGD, loss_Momentum, loss_RMSprop, loss_Adam]

# 执行训练
for epoch in xrange(EPOCH):
    for step, (batch_x, batch_y) in enumerate(loader):
        var_x = Variable(batch_x)
        var_y = Variable(batch_y)
        for net, optimizer, loss_history in zip(nets, optimizers, losses):
            # 对x进行预测
            prediction = net(var_x)
            # 计算损失
            loss = loss_func(prediction, var_y)
            # 每次迭代清空上一次的梯度
            optimizer.zero_grad()
            # 反向传播
            loss.backward()
            # 更新梯度
            optimizer.step()
            # 保存loss记录
            loss_history.append(loss.data[0])

# 画图
labels = ['SGD', 'Momentum', 'RMSprop', 'Adam']
for i, loss_history in enumerate(losses):
    plt.plot(loss_history, label = labels[i])
plt.legend(loc = 'best')
plt.xlabel('Steps')
plt.ylabel('Loss')
plt.ylim((0, 0.2))
plt.show()

由于时间紧促,个人对这部分并未全然了解,只是将整个知识结构加以整理,之后随着进一步的学习会更新这篇文章。

参考:
PyTorch学习之十种优化函数
如何选择优化器 optimizer
PyTorch基本用法(九)——优化器
“花书”《深度学习》

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值