文章目录
0.0梯度下降法和最速下降法的细微差别
参考:https://blog.csdn.net/Timingspace/article/details/50963564
如果,Hessian矩阵不是对角阵,那么我们需要求出矩阵的特征值,最大特征值和最小的特征值之比(这个值叫做condition number)如果不大,则梯度下降法会有很好的效果。
1.0梯度下降的三种方式:
1.1批量梯度下降(Batch Gradient Descent)
这种方式在梯度下降的每一步中,我们都用到了所有的训练样本,也就是说,我们需要计算每一个
下损失函数的梯度,在梯度下降中,在计算微分时,我们需要进行求和运算。但是当样本量很大时,这种方式的训练速度是很慢的,并不能让人满意。
推广:
由于我们有m个样本,这里求梯度的时候就用了所有m个样本的梯度数据。
随机梯度下降(Stochastic Gradient Descent)
这种方式和批量梯度下降法是两个极端,批量梯度下降每次采用所有的数据来梯度下降,随机梯度下降每次用一个样本来梯度下降。所以说,随机梯度在每一步的梯度计算上只随机选取训练集中的一个样本。由于每一次的操作都使用了非常少的数据,这样使得算法变得非常快。而批量梯度的算法就很慢。
对于训练速度来说,随机梯度下降法由于每次仅仅采用一个样本来迭代,训练速度很快。
但是对于准确度来说,随机梯度下降法每次训练仅仅用一个样本决定梯度方向,可能得到局部最小值。
对于收敛速度来说,由于随机梯度下降法一次迭代一个样本,导致迭代方向变化很大,不能很快的收敛到局部最优解。
推广:
小批量梯度下降(MiniBatch Gradient Descent)
小批量梯度下降法是批量梯度下降法和随机梯度下降法的折衷,也就是对于m个样本,我们采用x个样子来迭代,1<x<m。一般可以取x=10,当然根据样本的数据,可以调整这个x的值。
个样本来近似计算损失函数。在每次迭代时要优化的目标函数变为:
转变成
其中 是对单个训练样本 (X_i,Y_i)的损失函数, 是需要学习的参数。
已经证明,随机梯度下降法在数学期望的意义下收敛,即随机采样产生的梯度的期望值是真实的梯度。因为每次迭代时的目标函数实际上是不一样的,因此随机梯度下降法并不能保证每次迭代时函数值一定下降。
对应的更新公式是:
推广
这种方式每次迭代使用一个以上又不是全部的样本。它的优点是:使用多个样本相比随机梯度提高了估计的精度。
缺点是:同SGD一样,每次梯度估计的方向不确定,可能需要很长时间接近最小值点,不会收敛,通常在使用MBGD之前先将数据集随机打乱,然后再划分Mini-Batch,所以MBGD有时也成SGD。只是Mini-Batch大小的选择通常使用2的幂数,可以获得更少的运行时间。
2.0梯度下降优化中,是分析如何加速算法的收敛,以及在收敛过程中如何防止其发生震荡
参考:https://www.jianshu.com/p/1ca0a0307695
背景: 预测函数为h(x) = m * x + b
用数学知识直接解出m和b,就可以得到L的方程。梯度下降中滑动更新参数m和参数b
参考:https://www.jianshu.com/p/dc258291d6a8
本文知识点:
学习率因子
冲量因子
衰减因子
第一个实验: 学习率因子对搜索过程的影响
我们知道在梯度下降过程中,我们通过定义学习率来表示每一次迭代下滑的距离(也就是步长),我们用learn_rate来表示,那么通过参数的梯度和学习率,参数的更新公式为:
对于这个式子,我们还可以用经典物理运动学去加以理解,我们在研究一个变速运动时,因为加速度是非线性变化的,所以我们用 表示当前时刻为t的瞬时速度,那么在下个t+1时刻,速度产生的变化是速度的微元dv,那么下个时刻(幅度) 的瞬时速度就可以表示为:
现在开始做第一个实验,我们假设目标函数F(x) = x * x + 2 * x + 10,初中生都知道这是一个凸函数,存在极小值且极小值也是最小值,其一阶导数为F’(x) = 2 * x + 2
通过导数知识,我们知道F(x)在x = -1处取得极小值(最小值),且极小值为9。
下图是带学习率因子的梯度下降法,我们将学习率作为参数变量传递进来.
现在我们初始搜索点在-5处,迭代周期为10来做梯度下降,然后分别设置不同的学习率为0.1,0.3,0.8来观察实验结果:
学习率为1,1.01:
已知极致是9,上图显示最终的逼近值。
可得到:(1)如果学习率较小,收敛到正确结果的速度会比较慢;(2)如果学习率较大,容易在搜索过程中发生震荡;(3)学习率的调整,会让梯度向反方向变化
第二个实验: 冲量法
冲量是一个过程量,来自于经典物理学,表示一个随时间改变的力对时间的累积效应,即力在时间上的积分
我们知道在普通的梯度下降过程中,每次参数x的更新量与学习率和梯度下降量有关系,如果我们想让收敛过程加快,那么我们需要让参数x的更新量做相应的加速运动;如果我们想防止减少震荡的频率,那么我们需要让参数x的更新量做相应的减速运动
最直接的改进是为迭代公式加上动量项,动量项累积了之前的权重更新值,加上此项之后的参数更新公式为 动量项累积了之前的梯度信息,类似于保持行走时的惯性,以避免来回震荡,加快收敛速度。
我们使用冲量的定义,在每次迭代更新过程中,将【参数的更新增量】等于【梯度下降量dv,和上一次的更新量v乘以一个介于[0,1]之间的因子momentum产生的时间累积冲量p】来求和。即最终增量v= dv + p,其中p = v * momentum,同样momentum因子我们作为参数传递进来。
为了研究不同的冲量因子在不同的学习率下的表现,我们将学习率和冲量因子分别设置为learnRate = [0.01, 0.1, 0.6, 0.9]和momentum = [0.0, 0.1, 0.5, 0.9],初始搜索点在-5处,迭代周期为10来做梯度下降实验
冲量因子分别设置为learnRate = [0.01, 0.5, 1,1.1]和momentum = [0.0, 0.1, 0.5, 0.9]
上图中,每一行代表在相同的学习率下,不同的冲量因子对梯度收敛的影响,其中最左侧表示不考虑冲量因子
至此,我们可以总结出在迭代周期不变的情况下:
当学习率比较小时,适当的冲量因子可以对收敛过程起到加速的作用
当学习率比较大时,适当的冲量因子可以减小震荡的频率
当学习率朝着反方向运行时,适当的冲量因子可以纠正错误
当冲量因子较大时,原本能够正确收敛的结果会偏离结果,造成刹不住车而跑过头
第三个实验: 学习率衰减因子
为了研究如何加速收敛以及如何防止搜索过程中发生震荡,我们引入了调整学习率,和使用冲量的原理来加速或减速收敛过程,但是冲量原理会使得收敛过快从而偏离最优结果,为了解决这个问题,接下来我们继续研究第三个超参数:衰减
从上面两个实验可以看出,当学习率较大的时候,容易发生震荡,根本原因无非就是搜索的幅度太大
可以试想,如果在迭代过程中,我们期望每次下滑的幅度都在不断的变慢,直到趋于0不再下滑,即学习率的大小随着迭代周期的变化而不断衰减,那么就可以不断减少震荡的频率。故伴随着学习率learnRate的衰减因子decay由此诞生,decay的作用就是不断的衰减learnRate
我们定义decay介于[0.0,1.0]之间,i表示迭代的次数,从公式上可以看出:
当decay>0时,learnRate属于减函数
当decay=0时,learnRate保持不变
decay越小,learnRate衰减的越慢,做加速度减小的加速运动
decay越大,learnRate衰减的越快,做加速度递增的加速运动
我们在迭代过程中,保证学习率均在衰减,同样为了研究不同的衰减因子的影响,我们把decay作为参数传递进来
为了研究不同的学习率衰减因子在不同的学习率下的表现,我们将学习率和学习率衰减因子分别设置为learnRate = [0.1,0.3,0.9,0.99]和decay= [0.0,0.01,0.5,0.9],初始搜索点在-5处,迭代周期为10来做梯度下降实验
至此,我们可以总结出在迭代周期不变的情况下:
decay越大,学习率衰减的越快
当学习率较大时候,震荡的频率和decay的增加成反比,即decay确实可以减缓震荡
当decay设置过大,可能会造成提前收敛,从而没有达到极值点
总结一下这三个超参数实验:
而整个梯度下降过程中,算法的收敛受到三个超参数—学习率,冲量因子和学习率衰减因子作用的影响,适当的参数组合,可以让算法收敛的速度加快,而且不容易发生震荡。
# 梯度下降法(沿着目标函数梯度下降的方向搜索极小值)
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['FangSong']
plt.rcParams['axes.unicode_minus'] = False
# 目标函数 x^2 + 2 * x + 10
def func(x):
return np.square(x) + 2 * x + 10
# 目标函数的一阶导数
def dfunc(x):
return 2 * x + 2
# 学习率因子-梯度下降
def gd_learnrate(x_start, dfunc, epochs, learnrate):
y = np.zeros(epochs + 1)
y[0] = x_start
for i in range(epochs):
y[i+1] = y[i] - learnrate * dfunc(y[i])
return y
# 冲量因子-梯度下降
def gd_momentum(x_start, dfunc, epochs, learnrate, momentum):
y = np.zeros(epochs + 1)
y[0] = x_start
v = 0
for i in range(epochs):
p = v * momentum # 速度变化过程产生的冲量
dv = -learnrate * dfunc(y[i]) # 本次的梯度下降量
v = dv + p # 本次的速度增量,受到冲量p的影响,做加速或者减速运动
y[i + 1] = y[i] + v
return y
# 学习率衰减因子-梯度下降
def gd_decay(x_start, dfunc, epochs, learnrate, decay):
y = np.zeros(epochs + 1)
y[0] = x_start
for i in range(epochs):
learnrate_decay = learnrate * 1.0 / (1.0 + decay * i)
y[i + 1] = y[i] - learnrate_decay * dfunc(y[i])
return y
# 测试学习率因子实验
def do_gd_learnrate():
line_x = np.linspace(-5, 5, 100)
line_y = func(line_x)
x_start, epochs = -5, 20
learnRate = [0.1,0.3, 0.6]
color = ['r', 'g', 'y']
for i in range(len(learnRate)):
x = gd_learnrate(x_start, dfunc, epochs, learnrate=learnRate[i])
plt.subplot(1, 3, i + 1)
plt.plot(line_x, line_y, c='b')
plt.plot(x, func(x), c=color[i], label='lr={}'.format(learnRate[i]))
plt.scatter(x, func(x), c=color[i])
print(func(x))
plt.legend()
plt.show()
# 测试冲量因子实验
def do_gd_momentum():
line_x = np.linspace(-5, 5, 100)
line_y = func(line_x)
x_start, epochs = -5, 10
learnRate = [0.01, 0.1, 0.6, 0.9]
momentum = [0.0, 0.1, 0.5, 0.9]
color = ['k', 'r', 'g', 'y']
row, col = len(learnRate), len(momentum)
for i in range(row):
for j in range(col):
x = gd_momentum(x_start, dfunc, epochs, learnrate=learnRate[i], momentum=momentum[j])
plt.subplot(row, col, i * col + j + 1)
plt.plot(x, func(x), c=color[i], label='lr={}, mo={}'.format(learnRate[i], momentum[j]))
plt.plot(line_x, line_y, c='b')
plt.scatter(x, func(x), c=color[i])
plt.legend()
plt.show()
# 测试学习率衰减因子实验
def do_gd_decay():
line_x = np.linspace(-5, 5, 100)
line_y = func(line_x)
x_start, epochs = -5, 10
learnRate = [0.1, 0.3, 0.9, 0.99]
decay = [0.0, 0.01, 0.5, 0.9]
color = ['k', 'r', 'g', 'y']
row, col = len(learnRate), len(decay)
for i in range(row):
for j in range(col):
x = gd_decay(x_start, dfunc, epochs, learnrate=learnRate[i], decay=decay[j])
plt.subplot(row, col, i * col + j + 1)
plt.plot(x, func(x), c=color[i], label='lr={}, de={}'.format(learnRate[i], decay[j]))
plt.plot(line_x, line_y, c='b')
plt.scatter(x, func(x), c=color[i])
plt.legend()
plt.show()
if __name__ == "__main__":
#do_gd_learnrate()
do_gd_momentum()
#do_gd_decay()
2.4变种算法
参考:https://zhuanlan.zhihu.com/p/32626442
2.4.1 AdaGrad的为自适应梯度,即adaptive gradient算法
是梯度下降法最直接的改进。唯一不同的是,AdaGrad根据前几轮迭代时的历史梯度值来调整学习率,参数更新公式为:
2.4.2 AdaDelta算法
也是梯度下降法的变种,在每次迭代时也利用梯度值构造参数的更新值。假设要优化的参数为x,梯度下降法第t次迭代时计算出来的参数梯度值为 。算法首先初始化如下两个向量为0向量:
2.4.3 Adam算法
全称为adaptive moment estimation,它由梯度项构造了两个向量m和v,它们的初始值为0,更新公式为:
2.4.4 NAG算法是一种凸优化方法
由Nesterov提出。和标准梯度下降法的权重更新公式类似,NAG算法构造一个向量v,初始值为0。v的更新公式为:
参数的更新公式为:
与带动量项的SGD相比NAG只是计算梯度时用的参数值不同,NAG计算误差梯度时考虑了动量项,使用的是
,其他都是一样的。
2.4.5 RMSProp算法
是标准梯度下降法的变种,它由梯度值构造一个向量MS,初始化为0,更新公式为:
参数更新公式为:
其中δ是人工设定的参数。这种方法通过梯度的历史信息来生成参数更新值的权重系数。
可视化分析
3.0 知识点
参考:链接:https://juejin.im/post/5ab60bc66fb9a028da7c7b68
学习梯度,需要一定的数学知识:导数(Derivative)、偏导数(Partial derivative)和方向导数(Directional derivative)。
1. 导数
导数的定义如下:
反应的是函数
在某一点处沿x轴正方向的变化率
2. 偏导数
偏导数的定义如下:
可以看到,导数与偏导数的本质都是一样的,当自变量的变化量趋于0时,函数值的变化量与自变量变化量比值的极限。直观的说,偏导数也就是函数在某一点上沿坐标轴正方向的变化率。
导数与偏导数的区别
导数: 指的是一元函数中,函数 在某一点处沿x轴正方向的变化率
偏导数:指的是多元函数中,函数 在某一点处沿某一坐标轴 正方向的变化率.
3. 方向导数
方向导数的定义如下:
导数与偏导数均为沿坐标轴正方向讨论函数的变化率,而方向导数,顾名思义,讨论函数在任意方向的变化率。即:某一点在某一趋近方向上的导数值
通俗的解释是:
我们不仅要知道函数在坐标轴正方向上的变化率(即偏导数),而且还要设法求得函数在其它特定方向上的变化率,而方向导数就是函数在其它特定方向上的变化率。
4. 梯度
梯度的定义如下:
梯度的存在,为了回答一个问题:
函数在变量空间的某一点处,沿着哪一个方向有着最大的变化率
梯度的文字定义如下:
函数在某一点的梯度是这样一个向量,它的方向与取得最大方向导数的方向一致,它的模为方向导数的最大值。
注意:
梯度是一个向量,有方向有大小
梯度的方向是最大方向导数的方向
梯度的值的最大方向导数的值
梯度即函数在某一点最大的方向导数,函数沿梯度方向,函数的变化率最大。
4.1 导数与梯度关系
参考:https://zhuanlan.zhihu.com/p/36902908
由于实际应用中一般都是多元函数,因此我们跳过一元函数,直接介绍多元函数的情况。梯度是导数对多元函数的推广,它是多元函数对各个自变量偏导数形成的向量。多元函数的梯度定义为:
其中∇ 称为梯度算子,它作用于一个多元函数,得到一个向量 。下面是计算函数梯度的一个例子:
梯度为0只是函数取极值的必要条件而不是充分条件
可导函数在某一点处取得极值的必要条件是梯度为0,梯度为0的点称为函数的驻点,这是疑似极值点。需要注意的是,梯度为0只是函数取极值的必要条件而不是充分条件,即梯度为0的点可能不是极值点。
至于是极大值还是极小值,要看二阶导数/Hessian矩阵,这是由函数的二阶偏导数构成的矩阵。这分为下面几种情况:
如果Hessian矩阵正定,函数有极小值
如果Hessian矩阵负定,函数有极大值
如果Hessian矩阵不定,则不是极值点(鞍点)
Hessian矩阵可以看做是一元函数的二阶导数对多元函数的推广
这和一元函数的结果类似,Hessian矩阵可以看做是一元函数的二阶导数对多元函数的推广 。一元函数的极值判别法为,假设在某点处导数等于0,则:
如果二阶导数大于0,函数有极小值
如果二阶导数小于0,函数有极大值
如果二阶导数等于0,情况不定
在这里我们可能会问:直接求函数的导数/梯度,然后令导数/梯度为0,解方程,问题不就解决了吗?事实上没这么简单,因为这个方程可能很难解。
对于有指数函数,对数函数,三角函数的方程,我们称为超越方程,求解的难度并不比求极值本身小。
工程上实现时通常采用,它从一个初始点开始,反复使用某种规则从
移动到下一个点
,构造这样一个数列,直到收敛到梯度为0的点处。即有下面的极限成立:
这些规则一般会利用一阶导数信息即梯度;或者二阶导数信息即Hessian矩阵。这样迭代法的核心是得到这样的由上一个点确定下一个点的迭代公式:
只要没有到达梯度为0的点,则函数值会沿着序列 [公式] 递减,最终会收敛到梯度为0的点,这就是梯度下降法。迭代终止的条件是函数的梯度值为0(实际实现时是接近于0),此时认为已经达到极值点。注意我们找到的是梯度为0的点,这不一定就是极值点。
梯度下降法只需要计算函数在某些点处的梯度,实现简单,计算量小。
5. 梯度下降法
既然在变量空间的某一点处,函数沿梯度方向具有最大的变化率,那么在优化目标函数的时候,自然是沿着负梯度方向去减小函数值,来达到我们的优化目标
如何沿着负梯度方向减小函数值呢?因为梯度是偏导数的集合,如下:
由于梯度和偏导数均为向量,由向量的运算法则可知,我们在每个变量轴上减小对应的变量值即可,梯度下降算法可描述为:
Repeat {
}
在这里,我们还需要了解几个概念:
5.1. 步长(learning rate)(学习速度)
步长决定了在梯度下降过程中,每一步沿梯度负方向前进的长度。
5.2. 特征(feature)
特征值的是样本输入部分,比如两个单特征样本则第一个样本的特征为
,第一个样本的输出为
5.3. 假设函数(hypothesis function)
在监督学习中,为了拟合输入样本,而使用假设函数
,记作.比如对于单个特征的m个样本
,可以采用拟合函数如下:
5.4. 损失函数(loss function)
为了评估模型拟合的好坏,通常损失函数来度量拟合的程度。损失函数极小化,意味着拟合程度最好,对应的模型参数即为最优参数。在线性回归中,损失函数通常为样本输出和假设函数的差取平方,比如对于m个样本
采用线性回归,损失函数为::
这里的
是为了方便求导
其中
表示第
个样本特征,表示第
个样本对于的输出
参考:https://blog.csdn.net/xiazdong/article/details/7950084
问题:鞍点
鞍点是指梯度为0,Hessian矩阵既不是正定也不是负定,即不定的点。
对于凸优化问题,不会遇到上面的局部极小值与鞍点问题,即梯度下降法一定能找到全局最优解。
下面是鞍点的一个例子,假设有函数:
显然在(0, 0)这点处不是极值点,但梯度为0,下面是梯度下降法的运行结果:
在这里,梯度下降法遇到了鞍点,认为已经找到了极值点,从而终止迭代过程,而这根本不是极值点。
提示:梯度下降运用到线性回归
梯度下降能够求出一个函数的最小值;
线性回归需要求出,使得cost function的最小;
因此我们能够对cost function运用梯度下降,即将梯度下降和线性回归进行整合,如下图所示:
梯度下降是通过不停的迭代。
6. 梯度下降算法详细
梯度下降法的算法可以有代数法和矩阵法(也称向量法)两种表示。
代数法
矩阵法(也称向量法)
参考:https://www.cnblogs.com/pinard/p/5970503.html
参考:https://www.jianshu.com/p/c7e642877b0e
6.1 代数法表示:
6.1.1下面用线性回归的例子来具体描述梯度下降
这种方法是:批量梯度下降(Batch Gradient Descent),速度是真的慢!!!而且效果一般。。。。
6.2 梯度下降法的矩阵方式描述
7.13.4 梯度下降的算法调优
在使用梯度下降时,需要进行调优。哪些地方需要调优呢?
1. 算法的步长选择。
在前面的算法描述中,我提到取步长为1,但是实际上取值取决于数据样本,可以多取一些值,从大到小,分别运行算法,看看迭代效果,如果损失函数在变小,说明取值有效,否则要增大步长。前面说了。步长太大,会导致迭代过快,甚至有可能错过最优解。步长太小,迭代速度太慢,很长时间算法都不能结束。所以算法的步长需要多次运行后才能得到一个较为优的值。
2. 算法参数的初始值选择。
初始值不同,获得的最小值也有可能不同,因此梯度下降求得的只是局部最小值;当然如果损失函数是凸函数则一定是最优解。由于有局部最优解的风险,需要多次用不同初始值运行算法,关键损失函数的最小值,选择损失函数最小化的初值。
3.Feature Scaling(常用:归一化方法)
此种方法应用于梯度下降,为了加快梯度下降的执行速度;
思想:将各个feature的值标准化,使得取值范围大致都在-1<=x<=1之间;
由于样本不同特征的取值范围不一样,可能导致迭代很慢,为了减少特征取值的影响,可以对特征数据归一化。常用的方法是Mean Normalization,
也可以表述为:
对于每个特征x,求出它的期望
和标准差
,然后转化为:
标准差,数学符号σ,在概率统计中最常使用作为测量一组数值的离散程度之用。标准差定义:为方差开算术平方根,反映组内个体间的离散程度;标准差与期望值之比为标准离差率。测量到分布程度的结果,原则上具有两种性质:为非负数值; 与测量资料具有相同单位。
这样特征的新期望为0,新方差为1,迭代速度可以大大加快。
举个例子:
举个实际的例子,
有两个Feature:
(1)size,取值范围0~2000;
(2)bedroom,取值范围0~5;
则通过feature scaling后,
8.0 举例:多变量线性回归
原文链接:https://blog.csdn.net/xiazdong/article/details/7950084
前面我们只介绍了单变量的线性回归,即只有一个输入变量,现实世界不可能这么简单,因此此处我们要介绍多变量的线性回归;
举个例子:
房价其实由很多因素决定,比如size、number of bedrooms、number of floors、age of home等,这里我们假设房价由4个因素决定,如下图所示:
这里我们可以定义出多变量线性回归的模型:
Cost function如下: