这里写目录标题

  • 1. 参数优化
    • 1.1 随机梯度下降法(SGD)
    • 1.2 Momentum 动量法
    • 1.3 AdaGrad
    • 1.4 RMSprop
    • 1.5 Adam
1. 参数优化

本文总结一下ANN参数更新的各种方法
一些可能用到的数学公式数学公式

1.1 随机梯度下降法(SGD)

​ 我们在以前的章节中使用随机梯度下降法(SGD)更新参数,在此,我们将提出其他的优化参数的方法,并指出SGD的一些缺点。

​ 我们通过一个故事理解一下寻找最优参数的过程:

​ 在这样苛刻的条件下,地面坡度显得尤为重要,通过脚底感受地面的倾斜状况,就是SGD的策略。勇敢 的探险家心里可能想着只要重复这一策略,总有一天可以到达“至深之地”。

1.1.2SGD

随机梯度下降法可以写成如下的式子

我们来实现一个名为SGD的类

class SGD:def __init__(self, lr=0.01):self.lr = lrdef update(self, params, grads):for key in params.keys():params[key] -= self.lr * grads[key]

如果大家学习过前面的章节,这段代码很容易理解。

update函数会不断被调用来更新参数。

我们可以按如下方式训练神经网络(伪代码,不能运行)

network = TwoLayerNet(...)
optimizer = SGD()
for i in range(10000):...x_batch, t_batch = get_mini_batch(...) # mini-batchgrads = network.gradient(x_batch, t_batch)params = network.paramsoptimizer.update(params, grads)

这里optimizer表示“进行最优化的人”的意思,由它负责更新参数。

如此,功能模块就会变得很简单。

1.1.3 SGD的缺点

SGD很简单并且容易实现,但是在解决某些问题时可能没有效率,我们来举个例子,求出下面函数最小值。

我们可以通过matplotlib来认识一下这个函数。代码如下

import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import axes3ddef f(x,y):return 0.05*(x**2)+y**2#线框图
fig = plt.figure()
ax = plt.axes(projection='3d')
#X = np.linspace(-10,10,256)
#Y = np.linspace(-10,10,256)
X = np.linspace(-10,10)
Y = np.linspace(-10,10)
print(f(0,0))
X, Y = np.meshgrid(X, Y)#生成网格点坐标矩阵。
Z = f(X,Y)
plt.xlabel("x")
plt.ylabel("y")
ax.set_zlabel("z")ax.plot_wireframe(X, Y, Z, color='black')
ax.set_title('wireframe')
#等高线图
fig2 = plt.figure()
level = np.array([0.0,1,2,3])
C = plt.contour(X, Y, Z,levels=level)
plt.clabel(C,inline=True,fontsize=10)
plt.contour(X,Y,Z)
plt.xlabel("x")
plt.ylabel("y")plt.show()

不难从两个图片看出,Z值关于x和y都是向0点下降的,不过y的下降梯度要更大。

接下来我们用图来表示一下梯度。代码如下,求梯度的函数大家在前面的章节已经很熟悉了

import numpy as np
import matplotlib.pyplot as plt
from deeplearning.fuction import numerical_gradientdef f(x):return np.sum(0.05*x[0]**2+x[1]**2)x = np.arange(-10,10)
y = np.arange(-5,6)
X,Y = np.meshgrid(x,y)
X = X.flatten()
Y = Y.flatten()
a = np.array([X, Y])
grad = numerical_gradient(f,a)plt.figure()
plt.quiver(X, Y, -grad[0], -grad[1], angles="xy", color="#666666")  # ,headwidth=10,scale=40,color="#444444")
plt.xlim([-10, 10])
plt.ylim([-5, 5])
plt.xlabel('x0')
plt.ylabel('x1')
plt.grid()plt.draw()
plt.show()

那么,接下来我们用SGD来更新参数,首先实现一下SGD的optimizer。

class SGD:def __init__(self,lr = 0.01):self.lr = lrdef update(self,params,grads):for key in params.keys():params[key]-=self.lr * grads[key]
import numpy as np
import matplotlib.pyplot as plt
from optimizer import SGDdef f(x, y):return x**2 / 20.0 + y**2def df(x, y): #直接把导数函数写出来,省略求梯度了return x / 10.0, 2.0*yinit_pos = (-7.0, 2.0)
params = {}
params['x'], params['y'] = init_pos[0], init_pos[1]
grads = {}
grads['x'], grads['y'] = 0, 0x_history = []
y_history = []optimizer = SGD(0.95)
for i in range(30):x_history.append(params['x'])y_history.append(params['y'])grads['x'], grads['y'] = df(params['x'], params['y'])optimizer.update(params,grads)for i in range(len(x_history)):print(x_history[i],y_history[i])x = np.arange(-10,10,0.01)
y = np.arange(-5, 5, 0.01)
X,Y = np.meshgrid(x,y)
Z = f(X,Y)mask = Z > 7
Z[mask] = 0plt.plot(x_history, y_history, 'o-', color="red")
plt.contour(X, Y, Z)plt.ylim(-10, 10)
plt.xlim(-10, 10)
plt.plot(0, 0, '+')plt.xlabel("x")
plt.ylabel("y")
plt.title("SGD")plt.show()

在图中,SGD呈“之”字形移动。这是一个相当低效的路径。也就是说, SGD的缺点是,如果函数的形状非均向(anisotropic),比如呈延伸状,搜索 的路径就会非常低效。这段代码中间有一段循环打印,我们直接看打印的数据。

-7.0 2.0
-6.335 -1.7999999999999998
-5.733175 1.6199999999999997
-5.188523375 -1.4579999999999997
-4.695613654375 1.3121999999999998
-4.249530357209375 -1.18098
-3.8458249732744845 1.0628819999999997
-3.4804716008134085 -0.9565937999999994
-3.1498267987361346 0.8609344199999993
-2.8505932528562017 -0.7748409779999994
-2.5797868938348625 0.6973568801999994
-2.3347071389205505 -0.6276211921799995
-2.112909960723098 0.5648590729619996
-1.9121835144544037 -0.5083731656657995
-1.7305260805812355 0.4575358490992195
-1.566126102926018 -0.41178226418929753
-1.4173441231480464 0.37060403777036777
-1.282696431448982 -0.333543633993331
-1.1608402704613288 0.3001892705939978
-1.0505604447675025 -0.270170343534598
-0.9507572025145898 0.2431533091811382
-0.8604352682757038 -0.21883797826302437
-0.778693917789512 0.19695418043672192
-0.7047179955995084 -0.17725876239304972
-0.6377697860175552 0.15953288615374472
-0.5771816563458875 -0.14357959753837024
-0.5223493989930281 0.12922163778453322
-0.4727262060886904 -0.11629947400607987
-0.42781721651026483 0.10466952660547188
-0.3871745809417897 -0.09420257394492468

Process finished with exit code 0

很容易看出来y的值虽然在趋近于0但是它在正负之间反复横跳,x的值在缓慢的趋近于0。

竖直方向上,梯度就非常大,在水平方向上,梯度就相对较小,所以我们在设置学习率的时候就不能设置太大,为了防止竖直方向上参数更新太过了,这样一个较小的学习率又导致了水平方向上参数在更新的时候太过于缓慢,所以就导致最终收敛起来非常慢。

因此,我们需要比单纯朝梯度方向前进的SGD更聪 明的方法。SGD低效的根本原因是,梯度的方向并没有指向最小值的方向。

1.2 Momentum 动量法

​ Momentum是“动量”的意思,和物理有关。数学式子如下

新变量v对应物理上的速度。就像小球在地面上滚动一样

大家可以思考一下,小球还没到最低点时速度会越来越快,经过最低点后,速度会越来越慢上升,然后速度越来越快的下降,重复这个过程直到最低点时停下。

分析一下上面的式子,α是一个动量参数,我们通常把它设置为小于1的正数,例如0.9。v是上一次更新所加的参数。其他的我们很熟悉了

举个例子,假如上述小球在最低点的x值为0,左方向x值为负,右方向x值为正。初始值,x值为-3,v为0,学习率 * 梯度=-1,(假设α=0.9)。

第一次更新参数,v=0.9 * 0-(-1)=1,x = x+1=-2。

第二次更新参数,假设学习率*梯度仍是-1:v =0.9 * 1-(-1)= 1.9,x=x+v=-0.1

第三次更新参数,假设学习率*梯度仍是-1:v=0.9 * 1.9 = 1.71,x=x+v = 1.61

第四次更新参数,(方向相反了)学习率*梯度=1:v = 0.9 *1.71 - 1=0.539,x = x+v=3.149

我们可以明显看出来,如果方向不变,v的值会逐步增大,也就是更新幅度会越来越大,如果方向变了,v的值会变小,更新幅度会变小。相当于每次在进行参数更新的时候,都会将之前的速度考虑进来,每个参数在各方向上的移动幅度不仅取决于当前的梯度,还取决于过去各个梯度在各个方向上是否一致,如果一个梯度一直沿着当前方向进行更新,那么每次更新的幅度就越来越大,如果一个梯度在一个方向上不断变化,那么其更新幅度就会被衰减,这样我们就可以使用一个较大的学习率,使得收敛更快,同时梯度比较大的方向就会因为动量的关系每次更新的幅度减少。(这段黑字引自这里,我觉得说得很明白)

下面我们来说一下代码:

class Momentum:def __init__(self,lr = 0.01,momentum = 0.9):self.lr = lrself.momentum = momentumself.v = Nonedef update(self,params,grads):if self.v is None: #初始化v为一个和参数params一样的结构,元素都为0self.v = {}for key,val in params.items():self.v[key] = np.zeros_like(val)for key in params.keys():self.v[key] = self.momentum*self.v[key] - self.lr*grads[key]params[key] += self.v[key]

然后我们来看一下Momentum的最优化更新路径,代码和SGD的代码一样,只不过把optimizer=Momentum(0.1),然后换一下plt.title(“Momentum”)就好了,图形如下。为了看清楚一点,我给放大了

很明显Momentum要更加的“圆滑”。

1.3 AdaGrad

​ 神经网络的学习中,学习率(数学式中记为η)的值很重要。学习率过小, 会导致学习花费过多时间;反过来,学习率过大,则会导致学习发散而不能 正确进行。

​ 在关于学习率的有效技巧中,有一种被称为学习率衰减(learning rate decay)的方法,即随着学习的进行,使学习率逐渐减小。实际上,一开始“多” 学,然后逐渐“少”学的方法,在神经网络的学习中经常被使用。

​ AdaGrad会为参数的每个元素适当地调整学习率,与此同时进行学习 (AdaGrad的Ada来自英文单词Adaptive,即“适当的”的意思)。下面,让 我们用数学式表示AdaGrad的更新方法。

与SGD相比,多了一个h(圆圈表示对应矩阵元素的乘法,不是点积)。h存了以前的所有梯度值的平方和。然后,在更新参数时,通过乘以 ,就可以调整学习的尺度。这意味着, 参数的元素中变动较大(被大幅更新)的元素的学习率将变小。也就是说, 可以按参数的元素进行学习率衰减,使变动大的参数的学习率逐渐减小。

AdaGrad会记录过去所有梯度的平方和。因此,学习越深入,更新 的幅度就越小。实际上,如果无止境地学习,更新量就会变为 0, 完全不再更新。为了改善这个问题,可以使用 RMSProp 方法。 RMSProp方法并不是将过去所有的梯度一视同仁地相加,而是逐渐 地遗忘过去的梯度,在做加法运算时将新梯度的信息更多地反映出来。 这种操作从专业上讲,称为“指数移动平均”,呈指数函数式地减小 过去的梯度的尺度。

下面我们来用代码实现一下AdaDrad:

class AdaGrad:def __init__(self,lr = 0.01):self.lr = lrself.h = Nonedef update(self,params,grads):if self.h is None:#初始化hself.h={}for key,val in params.items():self.h[key] = np.zeros_like(val)for key in params:self.h[key] += grads[key]*grads[key]params[key] -= self.lr*grads[key]/(np.sqrt(self.h[key])+1e-7)#1e-7防止0被用作分母

同Momentum画图一样,我们画一下它的路径优化图:

效果是不是很棒!函数的取值高效地向着最小值移动。由于y轴方 向上的梯度较大,因此刚开始变动较大,但是后面会根据这个较大的变动按 比例进行调整,减小更新的步伐。因此,y轴方向上的更新程度被减弱,“之” 字形的变动程度有所衰减。

1.4 RMSprop

由于书上没有讲解,建议大家直接看吴恩达的deeplearning关于指数加权平均的部分,一共半个小时吧,应该没人讲的更好了,所以大家有理论不懂得地方推荐看他的视频。提醒大家注意视频中讲解β=0.9的时候是十天平均温度,β=0.98的时候是50天平均温度,大家要理解这是为什么。指数加权平均又叫指数移动平均,而移动的精髓就在此处。给大家个链接吧,因为b站吴恩达课程太多了,从p63开始。指数加权平均

了解理论之后直接用代码实现,和AdaGrad只有一部分不同。

class RMSprop:"""RMSprop"""def __init__(self, lr=0.01, decay_rate = 0.99):self.lr = lrself.decay_rate = decay_rateself.h = Nonedef update(self, params, grads):if self.h is None:self.h = {}for key, val in params.items():self.h[key] = np.zeros_like(val)for key in params.keys():self.h[key] *= self.decay_rateself.h[key] += (1 - self.decay_rate) * grads[key] * grads[key]params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)

此处用的lr=0.1,书中代码lr=0.01,但是0.01绘出来的图离中心点太远了,收敛的不够,下降率也就是α=0.98。比上面的AdaGrad圆滑了一点。

1.5 Adam

学习过计算机网络或者操作系统的了解,我们设计算法的时候最喜欢缝合怪了,就是把两种算法结合在一起,同时具备两者的优点。

Momentum参照小球在碗中滚动的物理规则进行移动,AdaGrad为参 数的每个元素适当地调整更新步伐。如果将这两个方法融合在一起会怎么样

Adam是2015年提出的新方法。它的理论有些复杂,直观地讲,就是融 合了Momentum和AdaGrad的方法。通过组合前面两个方法的优点,有望 实现参数空间的高效搜索。此外,进行超参数的“偏置校正”也是Adam的特征。吴恩达课程中RMSprop之后就讲Adam了,建议直接去看它的这部分理论课,7分钟左右,然后回来看代码。

class Adam:def __init__(self, lr=0.001, beta1=0.9, beta2=0.999):self.lr = lrself.beta1 = beta1 #动量法参数self.beta2 = beta2#RMSprop参数self.iter = 0self.m = None     #动量法的self.v = None     #RMSprop的def update(self, params, grads):if self.m is None:self.m, self.v = {}, {}for key, val in params.items():self.m[key] = np.zeros_like(val)self.v[key] = np.zeros_like(val)self.iter += 1lr_t = self.lr * np.sqrt(1.0 - self.beta2 ** self.iter) / (1.0 - self.beta1 ** self.iter)#偏差修正lr_t 把学习率和修正计算在一起了for key in params.keys():self.m[key] = self.beta1*self.m[key] + (1-self.beta1)*grads[key]self.v[key] = self.beta2*self.v[key] + (1-self.beta2)*(grads[key]**2)#self.m[key] += (1 - self.beta1) * (grads[key] - self.m[key])#吴恩达老师方法#self.v[key] += (1 - self.beta2) * (grads[key] ** 2 - self.v[key])#吴恩达老师方法params[key] -= lr_t * self.m[key] / (np.sqrt(self.v[key]) + 1e-7)

大家可以观察到这和吴恩达讲的不太一样,在计算mt和vt时都多减了一个mt-1,vt-1,下面注释的两行和吴恩达讲的一样,分别画出这两种计算方法的图如下

有没有发现一模一样,所以我猜测书上的办法可能是又修正了一个什么误差我们没学习到吧,不过我们按照吴恩达老师讲的就可以了,毕竟也没看出两者有什么差别。唯一不好理解的是在代码中,学习率和两个修正乘在一起了,大家用笔推一下就能理解了。三四行的公式而已。

上面我们介绍了SGD、Momentum、AdaGrad、Adam,RMSprop这5种方法,那 么用哪种方法好呢?非常遗憾,(目前)并不存在能在所有问题中都表现良好 的方法。这4种方法各有各的特点,都有各自擅长解决的问题和不擅长解决 的问题。 很多研究中至今仍在使用SGD。Momentum和AdaGrad也是值得一试 的方法。最近,很多研究人员和技术人员都喜欢用Adam。再以后的学习中主要使用SGD和Adam。

深度学习入门-ANN神经网络参数最优化问题(SGD,Momentum,AdaGrad,RMSprop,Adam)相关推荐

  1. 深度学习中常用的优化算法(SGD, Nesterov,Adagrad,RMSProp,Adam)总结

    深度学习中常用的优化算法(SGD, Nesterov,Adagrad,RMSProp,Adam)总结 1. 引言 在深度学习中我们定义了损失函数以后,会采取各种各样的方法来降低损失函数的数值,从而使模 ...

  2. 深度学习笔记:优化方法总结(BGD,SGD,Momentum,AdaGrad,RMSProp,Adam)

    深度学习笔记(一):logistic分类  深度学习笔记(二):简单神经网络,后向传播算法及实现  深度学习笔记(三):激活函数和损失函数  深度学习笔记:优化方法总结  深度学习笔记(四):循环神经 ...

  3. 优化方法总结(BGD,SGD,Momentum,AdaGrad,RMSProp,Adam)

    本文介绍常见的一阶数值优化算法,这些方法在现代神经网络框架(tensorflow, caffe, torch)中已经是标准配置. 问题 设系统参数为ω.对于样本i,其代价函数为Qi(ω).在n个样本组 ...

  4. 深度学习入门之神经网络的学习

    文章目录 从数据中学习 数据驱动 一种方案 训练数据和测试数据 损失函数 均方误差 交叉熵误差 mini-batch学习 数值微分 导数 数值微分的例子 偏导数 求解两个关于偏导数的例子 题目一 题目 ...

  5. 深度学习入门——前馈神经网络

    前馈神经网络作为深度学习基础中的基础,是很多同学入门深度学习的必经之路.由于马上要迎来考试复习周,在这里简单记录一下学习心得. 感知机模型 感知机(perceptron)是深度学习中最基本的元素,很多 ...

  6. 深度学习入门之神经网络

    神经网络 从感知机到神经网路 神经网络的例子 激活函数出场 激活函数 sigmoid函数 阶跃函数的实现 阶跃函数的图形 sigmoid函数的实现 sigmoid函数和阶跃函数的比较 非线性函数 Re ...

  7. 深度学习——02、深度学习入门——卷积神经网络

    神经网络框架 卷积层详解 卷积神经网络组成 1.输入层(INPUT) 2.卷积层(CONV) 3.激活函数(RELU) 4.池化层(POOL) 5.全连接层(FC) 卷积计算流程 将image划分为一 ...

  8. 深度学习入门之神经网络的学习思维导图

    第4章 神经网络的学习思维导图

  9. 深度学习入门之神经网络思维导图

    第3章 神经网络思维导图

最新文章

  1. plt转pdf软件_无须转Word也能轻松编辑PDF的软件,它来了!
  2. vscode设置默认新建html,VScode修改默认生成的HTML模板的方法
  3. openssl https 单向认证连接成功示例
  4. UML模型中的图-静态图【类图、对象图】
  5. linux 添加环境变量(php为例)
  6. oracle sga pga mysql_oracle实例内存(SGA和PGA)调整-xin
  7. Junit下获取src/test/resource路径
  8. sql进程意外终止_字节跳动五面都过了,竟然意外被刷了下来,问了hr原因竟说是。。。。。...
  9. SparkStreaming与kafka的结合
  10. C#反编译工具ilspy下载地址
  11. 埃氏筛法求区间内素数并输出
  12. 怎样解决迅雷下载时的版权限制-两招解决迅雷下载版权限制
  13. 数据表格之多表头设置
  14. Altium Designer基础知识
  15. 微信小程序-轮播图的实现
  16. 为什么有的人特别招蚊子?
  17. 史上最全Maven教程(一)
  18. java的setquality值多少_java使用JPEGEncodeParam类的setQuality方法进行高质量图片缩放类(转)...
  19. java的图片文件上传下载,多表新增,菜品信息分页
  20. Mysql 5.7.30-winx64 解压版安装教程

热门文章

  1. 推荐这几个硬核大佬,过年抢他们红包
  2. 4阶经典龙格库塔格式
  3. Android之控件阴影模糊效果死磕Paint.setShadowLayer()
  4. 诺贝尔经济学家的著作——值得读
  5. Android Studio开发环境
  6. 高质量图片无损压缩算法
  7. 将预训练模型应用于长文本阅读理解
  8. 渐开线齿轮齿形误差和齿向误差
  9. Altair HyperWorks Solvers 14.0.211 HotFix Win64 Linux64 2CD
  10. node.js源码编译安装(linux)