- 梯度下降算法也是先构建误差值的函数, 通过求误差值函数的最小值来达到误差最小的目的, 不过梯度下降是先随机取值, 然后求函数在该点的导数, 如果导数为正, 下一次取值向反方向移动, 如果导数为负, 正向移动, 直到导数取值极小时, 认定误差达到一个可以接受的范围, 然后导出相应的系数, 用于预测的模型。 # stochastic(随机)
- 梯度下降法是一点点去逼近最优解。
from sklearn.linear_model import SGDRegressor
sgd = SGDRegressor(penalty='l2',alpha=0, l1_ratio=0)
sgd.fit(X, y.reshape(-1,))
print('随机梯度下降求解的斜率是:',sgd.coef_)
print('随机梯度下降求解的截距是:',sgd.intercept_)
二 梯度下降
1、无约束最优化问题
无约束最优化问题(unconstrained optimizationproblem)指的是从一个问题的所有可能的备选方案中,选择出依某种指标来说是最优的解决方案。从数学上说,最优化是研究在一个给定的集合S上泛函的极小化或极大化问题。
无约束最优化问题(unconstrained optimizationproblem)指的是从一个问题的所有可能的备选方案中,选择出依某种指标来说是最优的解决方案。从数学上说,最优化是研究在一个给定的集合S上泛函的极小化或极大化问题:广义上,最优化包括数学规划、图和网络、组合最优化、库存论、决策论、排队论、最优控制等。狭义上,最优化仅指数学规划。
1.1、梯度下降
梯度下降法(Gradient Descent)是一个算法,但不是像多元线性回归那样是一个具体做回归任务的算法,而是一个非常通用的优化算法来帮助一些机器学习算法(都是无约束最优化问题)求解出最优解, 所谓的通用就是很多机器学习算法都是用梯度下降,甚至深度学习也是用它来求解最优解。所有优化算法的目的都是期望以最快的速度把模型参数θ求解出来,梯度下降法就是一种经典常用的优化算法。
之前我们令导数为 0,反过来求解最低点 θ 是多少,而梯度下降法是一点点去逼近最优解 !
1.2、梯度下降公式
这里梯度下降法的公式就是一个式子指导计算机迭代过程中如何去调整θ,可以通过泰勒公式一阶展开来进行推导和证明:
其中 表示学习率,gradient 表示梯度 。
有些公式,使用其他字母表示:
学习率一般都是正数,如果在山左侧(曲线左半边)梯度是负的,那么这个负号就会把 往大了调, 如果在山右侧(曲线右半边)梯度就是正的,那么负号就会把 往小了调。每次 调整的幅度就是 ,就是横轴上移动的距离。
1.3、学习率
根据我们上面讲的梯度下降公式,我们知道η是学习率,设置大的学习率目标值每次调整的幅度就大,设置小的学习率 目标值每次调整的幅度就小,然而如果步子迈的太大也会有问题,俗话说步子大了容易扯着蛋!学习率大,可能一下子迈过了,到另一边去了(从曲线左半边跳到右半边),继续梯度下降又迈回来, 使得来来回回震荡。步子太小呢,就像蜗牛一步步往前挪,也会使得整体迭代次数增加。
学习率的设置是一门学问,一般我们会把它设置成一个比较小的正整数,0.1、0.01、0.001、0.0001,都是常见的设定数值(然后根据情况调整)。
一般情况下学习率在整体迭代过程中是不变,但是也可以设置成随着迭代次数增多学习率逐渐变小,因为越靠近山谷我们就可以步子迈小点,可以更精准的走入最低点,防止走过。还有一些深度学习的优化算法会自己控制调整学习率这个值。
1.4、全局最优化
梯度下降的两个主要挑战:
- 若随机初始化,算法从左侧起步,那么会收敛到一个局部最小值,而不是全局最小值;
- 若随机初始化,算法从右侧起步,那么需要经过很长时间才能越过Plateau(函数停滞带,梯度很小),如果停下得太早,则永远达不到全局最小值;
1.5、梯度下降步骤
梯度下降流程就是“猜”正确答案的过程:
1、“瞎蒙”,Random 随机数生成θ,随机生成一组数值 ,期望 为 0 方差 为 1 的正太分布数据,防止局部最小值。
2、求梯度 g ,梯度代表曲线某点上的切线的斜率,沿着切线往下就相当于沿着坡度最陡峭的方向下降。
3、if g < 0, 变大,if g > 0, 变小。
4、判断是否收敛(convergence),如果收敛跳出迭代,如果没有达到收敛,回第 2 步再次执行2~4步。
收敛的判断标准是:随着迭代进行损失函数Loss,变化非常微小甚至不再改变,即认为达到收敛。
1.6、代码模拟梯度下降
使用梯度下降的方式求解:
import numpy as np
import matplotlib.pyplot as plt
f = lambda x : (x - 3.5)**2 -4.5*x + 10
d = lambda x :2*(x - 3.5) - 4.5 # 梯度 == 导数 ### 导函数
step = 0.1 # 梯度下降的步幅,比例,(学习率,幅度)
# 求解当x等于多少的时候,函数值最小。求解目标值:随机生成的
# 相等于:'瞎蒙' ----> 方法 ----> 优化
x = np.random.randint(0,12,size = 1)[0]
# 梯度下降,每下降一步,每走一步,目标值,都会更新。
# 更新的这个新值和上一步的值,差异,如果差异很小(万分之一)
# 梯度下降退出
last_x = x + 0.02 # 记录上一步的值,首先让last_x和x有一定的差异!!!
# 精确率,真实计算,都是有误差,自己定义
precision = 1e-4
print('+++++++++++++++++++++', x)
x_ = [x]
while True:
# 退出条件,精确度,满足了
if np.abs(x - last_x) < precision: # 计算数组各元素的绝对值
break
# 更新
last_x = x
x -= step*d(x) # 更新,减法:最小值 # 通过倒数计算下一个值
x_.append(x)
print('--------------------',x)
# 数据可视化
plt.rcParams['font.family'] = 'Kozuka Gothic Pro'
plt.figure(figsize=(9,6))
x = np.linspace(5.75 - 5, 5.75 + 5, 100)
y = f(x)
plt.plot(x,y,color = 'green')
plt.title('梯度下降',size = 18,pad = 15)
x_ = np.array(x_)
y_ = f(x_)
plt.scatter(x_, y_,color = 'red') # 画变化的点
plt.savefig('./5-梯度下降.jpg',dpi = 200)
函数的最优解是:5.75。你可以发现,随机赋值的变量 x ,无论大于5.75,还是小于5.75,经过梯度下降,最终都慢慢靠近5.75这个最优解!
2、梯度下降方法
2.1、三种梯度下降不同
梯度下降分三类:批量梯度下降BGD(Batch Gradient Descent)、小批量梯度下降MBGD(Mini-Batch Gradient Descent)、随机梯度下降SGD(Stochastic Gradient Descent)。
三种梯度下降的不同,主要体现在第二步中:
- BGD是指在每次迭代使用所有样本来进行梯度的更新。
- MBGD是指在每次迭代使用一部分样本来进行梯度的更新。
- SGD是指每次迭代随机选择一个样本来进行梯度更新。
三种梯度下降开始的方法是一样的,梯度下降步骤分四步:
1、随机赋值,Random 随机数生成 ,随机一组数值 。
2、求梯度 g ,梯度代表曲线某点上的切线的斜率,沿着切线往下就相当于沿着坡度最陡峭的方向下降。
3、if g < 0, 变大,if g > 0, 变小。
4、判断是否收敛 convergence,如果收敛跳出迭代,如果没有达到收敛,回第 2 步再次执行2~4步,收敛的判断标准是:随着迭代进行损失函数Loss,变化非常微小甚至不再改变,即认为达到收敛。
最小二乘法公式如下:
矩阵写法:
损失函数的导数:
2.2、批量梯度下降BGD
批量梯度下降法是最原始的形式,它是指在每次迭代使用所有样本来进行梯度的更新。
优点:
- 一次迭代是对所有样本进行计算,此时利用矩阵进行操作,实现了并行。
- 由全数据集确定的方向能够更好地代表样本总体,从而更准确地朝向极值所在的方向。当目标函数为凸函数时,BGD一定能够得到全局最优。
缺点:
- 当样本数目 n 很大时,每迭代一步都需要对所有样本计算,训练过程会很慢。
2.3、随机梯度下降SGD
随机梯度下降法不同于批量梯度下降,随机梯度下降是每次迭代使用一个样本来对参数进行更新。使得训练速度加快。批量梯度下降算法每次都会使用全部训练样本,因此这些计算是冗余的,因为每次都使用完全相同的样本集。而随机梯度下降算法每次只随机选择一个样本来更新模型参数,因此每次的学习是非常快速的。
优点:
- 由于不是在全部训练数据上的更新计算,而是在每轮迭代中,随机选择一条数据进行更新计算,这样每一轮参数的更新速度大大加快。
缺点:
- 准确度下降。由于即使在目标函数为强凸函数的情况下,SGD仍旧无法做到线性收敛。
- 可能会收敛到局部最优,由于单个样本并不能代表全体样本的趋势。
2.4、小批量梯度下降MBGD
小批量梯度下降,是对批量梯度下降以及随机梯度下降的一个折中办法。其思想是:每次迭代使用总样本中的一部分(batch_size)样本来对参数进行更新。这里我们假设 batch_size = 20,样本数 n = 1000 。实现了更新速度与更新次数之间的平衡。
相对于随机梯度下降算法,小批量梯度下降算法降低了收敛波动性, 即降低了参数更新的方差,使得更新更加稳定。相对于全量梯度下降,其提高了每次学习的速度。并且其不用担心内存瓶颈从而可以利用矩阵运算进行高效计算。
一般情况下,小批量梯度下降是梯度下降的推荐变体,特别是在深度学习中。每次随机选择2的幂数个样本来进行学习,例如:8、16、32、64、128、256。因为计算机的结构就是二进制的。但是也要根据具体问题而选择,实践中可以进行多次试验, 选择一个更新速度与更次次数都较适合的样本数。MBGD梯度下降迭代的收敛曲线更加温柔一些.
2.5、梯度下降优化
虽然梯度下降算法效果很好,并且广泛使用,但是不管用上面三种哪一种,都存在一些挑战与问题,我们可以从以下几点进行优化:
- 选择一个合理的学习速率很难。如果学习速率过小,则会导致收敛速度很慢。如果学习速率过大,那么其会阻碍收敛,即在极值点附近会振荡。
- 学习速率调整,试图在每次更新过程中, 改变学习速率。从经验上看,学习率在一开始要保持大些来保证收敛速度,在收敛到最优点附近时要小些以避免来回震荡。比较简单的学习率调整可以通过 学习率衰减(Learning Rate Decay)的方式来实现。假设初始化学习率为 ,在第 t 次迭代时的学习率 。常用的衰减方式为可以设置为 按迭代次数进行衰减,迭代次数越大,学习率越小! (两种常用的学习率衰减方式: 或者 , n为迭代次数)
- 模型所有的参数每次更新都是使用相同的学习速率。如果数据特征是稀疏的,或者每个特征有着不同的统计特征与空间,那么便不能在每次更新中每个参数使用相同的学习速率,那些很少出现的特征应该使用一个相对较大的学习速率。
- 对于非凸目标函数,容易陷入那些次优的局部极值点中,如在神经网路中。那么如何避免呢?一般使用随机梯度下降即可解决。在深度学习里,对梯度下降进行了很多改进,比如:自适应梯度下降。
- 轮次和批次
- 轮次:epoch,轮次顾名思义是把我们已有的训练集数据学习多少轮,迭代多少次。
- 批次:batch,批次这里指的的我们已有的训练集数据比较多的时候,一轮要学习太多数据, 那就把一轮次要学习的数据分成多个批次,一批一批数据的学习。
3、代码实战梯度下降
3.1、批量梯度下降BGD
这里我们使用了偏置项,即解决 。一元一次线性回归 问题。
import numpy as np
# 1、创建数据集X,y
X = np.random.rand(100, 1)
w,b = np.random.randint(1,10,size = 2)
y = w * X + b + np.random.randn(100, 1)
# 2、使用偏置项x_0 = 1,更新X
X = np.c_[X,np.ones((100, 1))]
# 3、创建超参数轮次
epoches = 10000
# 4、定义一个函数来调整学习率
t0, t1 = 5, 1000
def learning_rate_schedule(t):
return t0/(t+t1)
# 5、初始化 W0...Wn,标准正太分布创建W
θ = np.random.randn(2, 1)
# 6、判断是否收敛,一般不会去设定阈值,而是直接采用设置相对大的迭代次数保证可以收敛
for i in range(epoches):
# 根据公式计算梯度
g = X.T.dot(X.dot(θ) - y)
# 应用梯度下降的公式去调整 θ 值
learning_rate = learning_rate_schedule(i)
θ = θ - learning_rate * g
print('真实斜率和截距是:',w,b) # 原始值 # 真实斜率和截距是: 4 8
print('梯度下降计算斜率和截距是:',θ) # [[4.50247147] [7.75125646]]
3.2、随机梯度下降SGD
import numpy as np
# 1、创建数据集X,y
X = 2*np.random.rand(100, 1)
w,b = np.random.randint(1,10,size = 2)
y = w * X + b + np.random.randn(100, 1)
# 2、使用偏置项x_0 = 1,更新X
X = np.c_[X, np.ones((100, 1))]
# 3、创建超参数轮次、样本数量
epochs = 10000
n = 100
# 4、定义一个函数来调整学习率
t0, t1 = 5, 500
def learning_rate_schedule(t):
return t0/(t+t1)
# 5、初始化 W0...Wn,标准正太分布创建W
θ = np.random.randn(2, 1)
# 6、多次for循环实现梯度下降,最终结果收敛
for epoch in range(epochs):
# 在双层for循环之间,每个轮次开始分批次迭代之前打乱数据索引顺序
index = np.arange(n) # 0 ~99
np.random.shuffle(index)
X = X[index] # 打乱顺序
y = y[index]
for i in range(n):
X_i = X[[i]]
y_i = y[[i]]
g = X_i.T.dot(X_i.dot(θ)-y_i) # 回归公式
learning_rate = learning_rate_schedule(epoch*n + i)
θ = θ - learning_rate * g
print('真实斜率和截距是:',w,b)
print('梯度下降计算斜率和截距是:',θ)
3.3、小批量梯度下降MBGD
import numpy as np
# 1、创建数据集X,y
X = np.random.rand(100, 3)
w = np.random.randint(1,10,size = (3,1))
b = np.random.randint(1,10,size = 1)
y = X.dot(w) + b + np.random.randn(100, 1)
# 2、使用偏置项 X_0 = 1,更新X
X = np.c_[X, np.ones((100, 1))]
# 3、定义一个函数来调整学习率
t0, t1 = 5, 500
def learning_rate_schedule(t):
return t0/(t+t1)
# 4、创建超参数轮次、样本数量、小批量数量
epochs = 10000
n = 100
batch_size = 16
num_batches = int(n / batch_size)
# 5、初始化 W0...Wn,标准正太分布创建W
θ = np.random.randn(4, 1)
# 6、多次for循环实现梯度下降,最终结果收敛
for epoch in range(epochs):
# 在双层for循环之间,每个轮次开始分批次迭代之前打乱数据索引顺序
index = np.arange(n)
np.random.shuffle(index)
X = X[index]
y = y[index]
for i in range(num_batches):
# 一次取一批数据16个样本
X_batch = X[i * batch_size : (i + 1)*batch_size]
y_batch = y[i * batch_size : (i + 1)*batch_size]
g = X_batch.T.dot(X_batch.dot(θ)-y_batch) # 回归
learning_rate = learning_rate_schedule(epoch * n + i)
θ = θ - learning_rate * g
print('真实斜率和截距是:',w,b)
print('梯度下降计算斜率和截距是:',θ)