关于深度学习优化器 optimizer 的选择,你需要了解这些

47 篇文章 25 订阅
39 篇文章 2 订阅

在很多机器学习和深度学习的应用中,我们发现用的最多的优化器是 Adam,为什么呢?

下面是 TensorFlow 中的优化器,
https://www.tensorflow.org/api_guides/python/train
这里写图片描述

在 keras 中也有 SGD,RMSprop,Adagrad,Adadelta,Adam 等:
https://keras.io/optimizers/

我们可以发现除了常见的梯度下降,还有 Adadelta,Adagrad,RMSProp 等几种优化器,都是什么呢,又该怎么选择呢?

在 Sebastian Ruder 的这篇论文中给出了常用优化器的比较,今天来学习一下:
https://arxiv.org/pdf/1609.04747.pdf

本文将梳理:

  • 每个算法的梯度更新规则和缺点
  • 为了应对这个不足而提出的下一个算法
  • 超参数的一般设定值
  • 几种算法的效果比较
  • 选择哪种算法

优化器算法简述?

首先来看一下梯度下降最常见的三种变形 BGD,SGD,MBGD,
这三种形式的区别就是取决于我们用多少数据来计算目标函数的梯度,
这样的话自然就涉及到一个 trade-off,即参数更新的准确率和运行时间。

1. Batch gradient descent

梯度更新规则:
BGD 采用整个训练集的数据来计算 cost function 对参数的梯度:

θ=θαθJ(θ)

缺点:
由于这种方法是在一次更新中,就对整个数据集计算梯度,所以计算起来非常慢,遇到很大量的数据集也会非常棘手,而且不能投入新数据实时更新模型

我们会事先定义一个迭代次数 epoch,首先计算梯度向量 params_grad,然后沿着梯度的方向更新参数 params,learning rate 决定了我们每一步迈多大。

Batch gradient descent 对于凸函数可以收敛到全局极小值,对于非凸函数可以收敛到局部极小值。

2. Stochastic gradient descent

梯度更新规则:
和 BGD 的一次用所有数据计算梯度相比,SGD 每次更新时对每个样本进行梯度更新, 对于很大的数据集来说,可能会有相似的样本,这样 BGD 在计算梯度时会出现冗余, 而 SGD 一次只进行一次更新,就没有冗余,而且比较快,并且可以新增样本。

缺点:
SGD 因为更新比较频繁,会造成 cost function 有严重的震荡,此外SGD对噪声比较敏感。

这里写图片描述

BGD 可以收敛到局部极小值,当然 SGD 的震荡可能会跳到更好的局部极小值处。

当我们稍微减小 learning rate,SGD 和 BGD 的收敛性是一样的。

3. Mini-batch gradient descent

梯度更新规则:
MBGD 每一次利用一小批样本,即 n 个样本进行计算, 这样它可以降低参数更新时的方差,收敛更稳定, 另一方面可以充分地利用深度学习库中高度优化的矩阵操作来进行更有效的梯度计算。
和 SGD 的区别是每一次循环不是作用于每个样本,而是具有 n 个样本的Batch。

超参数设定值:
n 一般取值在 50~200

缺点:
Mini-batch gradient descent 不能保证很好的收敛性,

①learning rate 如果选择的太小,收敛速度会很慢,如果太大,loss function 就会在极小值处不停地震荡甚至偏离。

②有一种措施是先设定大一点的学习率,当两次迭代之间的变化低于某个阈值后,就减小 learning rate,不过这个阈值的设定需要提前写好,这样的话就不能够适应数据集的特点。此外,这种方法是对所有参数更新时应用同样的 learning rate,如果我们的数据是稀疏的,我们更希望对出现频率低的特征进行大一点的更新

③另外,对于非凸函数,还要避免陷于局部极小值处,或者鞍点处,因为鞍点周围的error 是一样的,所有维度的梯度都接近于0,SGD 很容易被困在这里。

鞍点:一个光滑函数的鞍点邻域的曲线,曲面,或超曲面,都位于这点的切线的不同边。
例如这个二维图形,像个马鞍:在x-轴方向往上曲,在y-轴方向往下曲,鞍点就是(0,0)

这里写图片描述

为了应对上面的三点挑战就有了下面这些算法。

[应对挑战 1]

4. Momentum(动量法)

SGD 在 ravines 的情况下容易被困住, ravines 就是曲面的一个方向比另一个方向更陡,这时 SGD 会发生震荡而迟迟不能接近极小值:

这里写图片描述

梯度更新规则:
Momentum 通过加入 γvt1 ,可以加速 SGD, 并且抑制震荡

vt=γvt1+αθJ(θ)

θ=θvt

当我们将一个小球从山上滚下来时,没有阻力的话,它的动量会越来越大,但是如果遇到了阻力,速度就会变小。
加入的这一项,可以使得梯度方向不变的维度上速度变快,梯度方向有所改变的维度上的更新速度变慢,这样就可以加快收敛并减小震荡。

超参数设定值:
一般 γ 取值 0.9 左右。

缺点:
这种情况相当于小球从山上滚下来时是在盲目地沿着坡滚,如果它能具备一些先知,例如快要上坡时,就知道需要减速了的话,适应性会更好。

5. Nesterov accelerated gradient(NAG)

梯度更新规则:
θγvt1 来近似当做参数下一步会变成的值,则在计算梯度时,不是在当前位置,而是未来的位置上

vt=γvt1+αθJ(θγvt1)

θ=θvt

超参数设定值:
γ 仍然取值 0.9 左右。

效果比较:
这里写图片描述

蓝色是 Momentum 的过程,会先计算当前的梯度,然后在更新后的累积梯度后会有一个大的跳跃。
而 NAG 会先在前一步的累积梯度上(brown vector)有一个大的跳跃,然后衡量一下梯度做一下修正(red vector),这种预期的更新可以避免我们走的太快。

NAG 可以使 RNN 在很多任务上有更好的表现。

目前为止,我们可以做到,在更新梯度时顺应 loss function 的梯度来调整速度,并且对 SGD 进行加速。

我们还希望可以根据参数的重要性而对不同的参数进行不同程度的更新。

[应对挑战 2]

6. Adagrad

这个算法就可以对低频的参数做较大的更新,对高频的做较小的更新,也因此,对于稀疏的数据它的表现很好,很好地提高了 SGD 的鲁棒性,例如识别 Youtube 视频里面的猫,训练 GloVe word embeddings,因为它们都是需要在低频的特征上有更大的更新。

梯度更新规则:

θt+1,i=θt,iαGt,ii+ϵgt,i

其中 gt,i 为:t 时刻参数 θi 的梯度; Gt 是个对角矩阵, (i,i) 元素就是 t 时刻参数 θi 的梯度 gt,i 的平方和。

Adagrad 的优点是减少了学习率的手动调节

超参数设定值:
一般 η 就取 0.01。

缺点:
它的缺点是分母会不断积累,这样学习率就会收缩并最终会变得非常小。

7. Adadelta

这个算法是对 Adagrad 的改进,

Δθt=αE[g2]t+ϵgt

和 Adagrad 相比,就是分母的 G 换成了过去的梯度平方E[g2]t的衰减平均值。

这个分母相当于梯度的均方根 root mean squared (RMS) ,所以可以用 RMS 简写:

Δθt=αRMS[g]tgt

其中 E 的计算公式如下,t 时刻的依赖于前一时刻的平均和当前的梯度:

E[g2]t=γE[g2]t1+(1γ)g2t

梯度更新规则:

此外,还将学习率 α 换成了 RMS[Δθ] ,这样的话,我们甚至都不需要提前设定学习率了:
这里写图片描述

超参数设定值:
γ 一般设定为 0.9,

7. RMSprop

RMSprop 是 Geoff Hinton 提出的一种自适应学习率方法。

RMSprop 和 Adadelta 都是为了解决 Adagrad 学习率急剧下降问题的。

梯度更新规则:
RMSprop 与 Adadelta 的第一种形式相同:

E[g2]t=0.9E[g2]t1+0.1g2t

θt+1=θtαE[g2]t+ϵgt

超参数设定值:
Hinton 建议设定 γ 为 0.9, 学习率 α为 0.001。

8. Adam

这个算法是另一种计算每个参数的自适应学习率的方法。目前在DL领域,是最常见的优化器。

除了像 Adadelta 和 RMSprop 一样存储了过去梯度的平方 vt 的指数衰减平均值 ,也像 momentum 一样保持了过去梯度 mt 的指数衰减平均值:
这里写图片描述

如果 mt vt 被初始化为 0 向量,那它们就会向 0 偏置,所以做了偏差校正,
通过计算偏差校正后的 mt 和 vt 来抵消这些偏差:
这里写图片描述

梯度更新规则:

θt+1=θtαvt+ϵmt

超参数设定值:
建议 β1 = 0.9,β2 = 0.999,ϵ = 10e−8

实践表明,Adam 比其他适应性学习方法效果要好。

效果比较?

下面看一下几种算法在鞍点和等高线上的表现:
这里写图片描述
这里写图片描述
上面两种情况都可以看出,Adagrad, Adadelta, RMSprop 几乎很快就找到了正确的方向并前进,收敛速度也相当快,而其它方法要么很慢,要么走了很多弯路才找到。

由图可知自适应学习率方法即 Adagrad, Adadelta, RMSprop, Adam 在这种情景下会更合适而且收敛性更好。

如何选择?

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

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

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

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

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

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

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

参考:

http://sebastianruder.com/optimizing-gradient-descent/index.html#fn:24
http://www.redcedartech.com/pdfs/Select_Optimization_Method.pdf
https://stats.stackexchange.com/questions/55247/how-to-choose-the-right-optimization-algorithm

  • 20
    点赞
  • 150
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
好的,以下是关于EEGNet模型的相关知识和Python代码的介绍: 1. EEGNet模型简介: EEGNet是一种用于处理脑电图(EEG)数据的深度学习模型,其主要目的是进行脑电信号的分类任务。EEG数据通常具有高维度、低信噪比和高相关性等特点,因此传统的机器学习算法在处理这种数据时存在一定的限制。为了解决这个问题,EEGNet采用了一种基于卷积神经网络(CNN)的新型架构,能够有效地学习到脑电信号的特征。 EEGNet模型由两个核心组件组成:深度可分离卷积层(Depthwise Separable Convolution Layer)和紧凑型卷积层(Pointwise Convolution Layer)。深度可分离卷积层用于捕捉脑电信号的时空特征,而紧凑型卷积层用于进行特征融合和分类。此外,EEGNet还采用了一种新型的数据预处理方法,即将EEG信号转换为时频图(Time-Frequency Maps),以便更好地反映信号的时域和频域特征。 2. EEGNet模型实现的Python代码: 以下是一个简单的EEGNet模型实现的Python代码示例,仅供参考: ```python import numpy as np from tensorflow import keras from tensorflow.keras import layers # 定义EEGNet模型结构 def EEGNet(input_shape, n_classes): input_layer = layers.Input(shape=input_shape) block1 = layers.Conv2D(filters=8, kernel_size=(1, 5), strides=1, padding='valid')(input_layer) block1 = layers.BatchNormalization()(block1) block1 = layers.DepthwiseConv2D(kernel_size=(2, 1), strides=(2, 1), depth_multiplier=8, padding='valid')(block1) block1 = layers.BatchNormalization()(block1) block1 = layers.Activation('elu')(block1) block1 = layers.SpatialDropout2D(rate=0.25)(block1) block2 = layers.Conv2D(filters=16, kernel_size=(1, 5), strides=1, padding='valid')(block1) block2 = layers.BatchNormalization()(block2) block2 = layers.DepthwiseConv2D(kernel_size=(2, 1), strides=(2, 1), depth_multiplier=16, padding='valid')(block2) block2 = layers.BatchNormalization()(block2) block2 = layers.Activation('elu')(block2) block2 = layers.SpatialDropout2D(rate=0.25)(block2) flatten = layers.Flatten()(block2) dense = layers.Dense(units=n_classes, activation='softmax')(flatten) model = keras.Model(inputs=input_layer, outputs=dense) return model # 加载数据集并进行训练 (x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data() x_train = np.expand_dims(x_train, axis=-1) x_test = np.expand_dims(x_test, axis=-1) y_train = keras.utils.to_categorical(y_train, num_classes=10) y_test = keras.utils.to_categorical(y_test, num_classes=10) model = EEGNet(input_shape=(28, 28, 1), n_classes=10) model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy']) model.fit(x_train, y_train, batch_size=128, epochs=10, validation_data=(x_test, y_test)) ``` 这个示例代码实现了一个简单的EEGNet模型,用于对MNIST手写数字数据集进行分类任务。在这个示例中,我们首先定义了EEGNet模型的结构,然后加载MNIST数据集并进行训练。在训练过程中,我们使用了Adam优化器和交叉熵损失函数,并在每个epoch结束时输出了模型在测试集上的准确率。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值