目录
一、在神经网络中为什么要使用激活函数呢?
激活函数:
ReLU
Leaky ReLU
Sigmoid
tanh
softmax
二、优化算法
1.批量梯度下降:
2.随机梯度下降:
3. mini-batch梯度下降:
4.改进的梯度下降算法:
5.Nesterov Accelerated Gradient
6.Adagrad (Adaptive gradient algorithm)
补充:指数加权平均
7.Adadelta
8.RMSprop
8.Adam:Adaptive Moment Estimation
三.如何选择优化算法
参考:
PyTorch框架中有一个非常重要且好用的包:torchvision,该包主要由3个子包组成,分别是:torchvision.datasets、torchvision.models、torchvision.transforms:https://pytorch.org/docs/master/torchvision/transforms.html#conversion-transforms。
现实中输入和输出之间的关系通常并非线性。如果神经网络的架构仅由线性算法组成,那么它很难计算出非线性行为。所以我们要在每层的结尾加上一个激活函数。
不同的激活函数有不同的特点。选取不当会导致梯度变得非常小,就是通常所说的梯度消失问题。
另外还存在一种相反的问题,就是梯度爆炸,当梯度值过大时,网络会变得非常不稳定。
ReLU
max(0,x):如果输入大于0,则输出等于输入。否则,输出为0。
它的输出范围从0到无穷大。这意味着它的输出可能会变得非常大,可能存在梯度爆炸问题。它还有个问题是左侧完全平坦,可能导致梯度消失。
ReLU计算简单,是神经网络内层最常用的激活函数。
Leaky ReLU
将ReLU函数的前半段用0.01x代替。
Sigmoid(逻辑函数)
1/(1+e-x)
这个函数非常适合将输入分为两类。它的形状很缓和,因此它的梯度能得到很好的控制。
主要的缺点是,在极端情况下,函数输出变得非常平坦。这意味着它存在梯度消失的问题。
tanh(双曲正切函数)
(2 / (1+e-2x)) - 1
它与Sigmoid非常相似。函数曲线更陡峭,因此它的输出也将更强大。缺点与Sigmoid类似。
tan -1反正切函数
softmax
e-x / Sum(e-x)
输出范围介于0和1之间。
Softmax将输入归一化为概率分布。它将输入压缩为0到1范围,就像Sigmoid。
它通常在多分类场景中的输出层,Softmax确保每个类的概率之和为1。
我们知道曲面上方向导数的最大值的方向就代表了梯度的方向,因此我们在做梯度下降的时候,应该是沿着梯度的反方向进行权重的更新,可以有效的找到全局的最优解。这个 的更新过程可以描述为
[n表示的是步长或者说是学习率(learning rate)]
for i in range(nb_epochs): params_grad = evaluate_gradient(loss_function, data, params) params = params - learning_rate * params_grad
在每次更新时用所有样本,要留意,在梯度下降中,对于 的更新,所有的样本都有贡献,也就是参与调整 .其计算得到的是一个标准梯度,对于最优化问题,凸问题,也肯定可以达到一个全局最优。因而理论上来说一次更新的幅度是比较大的。如果样本不多的情况下,当然是这样收敛的速度会更快啦。但是很多时候,样本很多,更新一次要很久,这样的方法就不合适啦。
在每次更新时用1个样本,可以看到多了随机两个字,随机也就是说我们用样本中的一个例子来近似我所有的样本,来调整θ,因而随机梯度下降是会带来一定的问题,因为计算得到的并不是准确的一个梯度,对于最优化问题,凸问题,虽然不是每次迭代得到的损失函数都向着全局最优方向, 但是大的整体的方向是向全局最优解的,最终的结果往往是在全局最优解附近。但是相比于批量梯度,这样的方法更快,更快收敛,虽然不是全局最优,但很多时候是我们可以接受的,所以这个方法用的也比上面的多。下图是其更新公式:
for i in range(nb_epochs): np.random.shuffle(data) for example in data: params_grad = evaluate_gradient(loss_function, example, params) params = params - learning_rate * params_grad
SGD 因为更新比较频繁,会造成 cost function 有严重的震荡。
BGD 可以收敛到局部极小值,当然 SGD 的震荡可能会跳到更好的局部极小值处。
当我们稍微减小 learning rate,SGD 和 BGD 的收敛性是一样的。
在每次更新时用b个样本,其实批量的梯度下降就是一种折中的方法,他用了一些小样本来近似全部的,其本质就是我1个指不定不太准,那我用个30个50个样本那比随机的要准不少了吧,而且批量的话还是非常可以反映样本的一个分布情况的。在深度学习中,这种方法用的是最多的,因为这个方法收敛也不会很慢,收敛的局部最优也是更多的可以接受!
for i in range(nb_epochs): np.random.shuffle(data) for batch in get_batches(data, batch_size=50): params_grad = evaluate_gradient(loss_function, batch, params) params = params - learning_rate * params_grad
超参数设定值: n 一般取值在 50~256
总的来说,随机梯度下降一般来说效率高,收敛到的路线曲折,但一般得到的解是我们能够接受的,在深度学习中,用的比较多的是mini-batch梯度下降。不过Mini-batch gradient descent 不能保证很好的收敛性,learning rate 如果选择的太小,收敛速度会很慢,如果太大,loss function 就会在极小值处不停地震荡甚至偏离。(有一种措施是先设定大一点的学习率,当两次迭代之间的变化低于某个阈值后,就减小 learning rate,不过这个阈值的设定需要提前写好,这样的话就不能够适应数据集的特点。)
Momentum 通过加入 γv_t−1 ,可以加速 SGD, 并且抑制震荡
当我们将一个小球从山上滚下来时,没有阻力的话,它的动量会越来越大,但是如果遇到了阻力,速度就会变小。
加入的这一项,可以使得梯度方向不变的维度上速度变快,梯度方向有所改变的维度上的更新速度变慢,这样就可以加快收敛并减小震荡。
用 θ−γv_t−1 来近似当做参数下一步会变成的值,(和动量方法进行对比)
蓝色是 Momentum 的过程,会先计算当前的梯度,然后在更新后的累积梯度后会有一个大的跳跃。
而 NAG 会先在前一步的累积梯度上(brown vector)有一个大的跳跃,然后衡量一下梯度做一下修正(red vector),这种预期的更新可以避免我们走的太快。
在更新梯度时顺应 loss function 的梯度来调整速度,并且对 SGD 进行加速。
我们还希望可以根据参数的重要性而对不同的参数进行不同程度的更新。
这个算法就可以对低频的参数做较大的更新,对高频的做较小的更新,也因此,对于稀疏的数据它的表现很好,很好地提高了 SGD 的鲁棒性,例如识别 Youtube 视频里面的猫,训练 GloVe word embeddings,因为它们都是需要在低频的特征上有更大的更新。
梯度更新规则:
其中 g 为:t 时刻参数 θ_i 的梯度
如果是普通的 SGD, 那么 θ_i 在每一时刻的梯度更新公式为:
但这里的 learning rate η 也随 t 和 i 而变:
其中 G_t
是个对角矩阵, (i,i) 元素就是 t 时刻参数 θ_i
的梯度平方和。
Adagrad 的优点是减少了学习率的手动调节
超参数设定值:一般η选取0.01
缺点:
它的缺点是分母会不断积累,这样学习率就会收缩并最终会变得非常小。
补充:指数加权平均
指数加权平均本质上就是一种近似求平均的方法。
我们现在直接给出公式:
通过上面表达式,我们可以看到,V100等于每一个时刻天数的温度值再乘以一个权值。本质就是以指数式递减加权的移动平均。各数值的加权而随时间而指数式递减,越近期的数据加权越重,但较旧的数据也给予一定的加权。(也就是式子中的θ100的权值比θ1的权值大)而在我们的普通平均数求法,它的每一项的权值都是一样的,如果有n项,权值都为1/n。
我们可以看到指数加权平均的求解过程实际上是一个递推的过程,那么这样就会有一个非常大的好处,每当我要求从0到某一时刻(n)的平均值的时候,我并不需要像普通求解平均值的作为,保留所有的时刻值,类和然后除以n。
而是只需要保留0-(n-1)时刻的平均值和n时刻的温度值即可。也就是每次只需要保留常数值,然后进行运算即可,这对于深度学习中的海量数据来说,是一个很好的减少内存和空间的做法。(也就是我们可以直接记录之前数据的平均值,然后利用当前数据乘以一个权值加上之前的平均值近似求平均值)
偏差修正
由以上证明可以看出,每个最新数据值,依赖于以前的数据结果。
一般令第一个数值为0,即v0=0;但此时初期的几个计算结果就会与真实的平均值有较大偏差,具体如下:
这个算法是对 Adagrad 的改进,
和 Adagrad 相比,就是分母的 G 换成了过去的梯度平方的衰减平均值,指数衰减平均值
这个分母相当于梯度的均方根 root mean squared (RMS),在数据统计分析中,将所有值平方求和,求其均值,再开平方,就得到均方根值 ,所以可以用 RMS 简写:
其中 E 的计算公式如下,t 时刻的依赖于前一时刻的平均和当前的梯度:
(指数加权平均的思路)
梯度更新规则:
此外,还将学习率 η 换成了 RMS[Δθ],这样的话,我们甚至都不需要提前设定学习率了:
超参数设定值: γ 一般设定为 0.9
RMSprop 是 Geoff Hinton 提出的一种自适应学习率方法。
RMSprop 和 Adadelta 都是为了解决 Adagrad 学习率急剧下降问题的,
梯度更新规则:
RMSprop 与 Adadelta 的第一种形式相同:(使用的是指数加权平均,旨在消除梯度下降中的摆动,与Momentum的效果一样,某一维度的导数比较大,则指数加权平均就大,某一维度的导数比较小,则其指数加权平均就小,这样就保证了各维度导数都在一个量级,进而减少了摆动。允许使用一个更大的学习率η)
超参数设定值:
Hinton 建议设定 γ 为 0.9, 学习率 η 为 0.001。
这个算法是另一种计算每个参数的自适应学习率的方法。相当于 RMSprop + Momentum
除了像 Adadelta 和 RMSprop 一样存储了过去梯度的平方 vt 的指数衰减平均值 ,也像 momentum 一样保持了过去梯度 mt 的指数衰减平均值:
如果 mt 和 vt 被初始化为 0 向量,那它们就会向 0 偏置,所以做了偏差校正,通过计算偏差校正后的 mt 和 vt 来抵消这些偏差:
梯度更新规则:
超参数设定值:
建议 β1 = 0.9,β2 = 0.999,ϵ = 10e−8
实践表明,Adam 比其他适应性学习方法效果要好。
如果数据是稀疏的,就用自适用方法,即 Adagrad, Adadelta, RMSprop, Adam。
RMSprop, Adadelta, Adam 在很多情况下的效果是相似的。
Adam 就是在 RMSprop 的基础上加了 bias-correction 和 momentum,
随着梯度变的稀疏,Adam 比 RMSprop 效果会好。
整体来讲,Adam 是最好的选择。
很多论文里都会用 SGD,没有 momentum 等。SGD 虽然能达到极小值,但是比其它算法用的时间长,而且可能会被困在鞍点。
如果需要更快的收敛,或者是训练更深更复杂的神经网络,需要用一种自适应的算法。
https://www.zhihu.com/question/264189719 如何理解随机梯度下降
http://sofasofa.io/tutorials/python_gradient_descent/ 自己动手用python写梯度下降
https://blog.csdn.net/tsyccnh/article/details/76270707 深度学习优化函数详解(4)-- momentum 动量法
https://www.cnblogs.com/guoyaohua/p/8542554.html