目录
一、梯度下降基本原理
通过测量量参数向量相关的误差函数的局部梯度,并并不断沿着降低梯度的方向调整,直到梯度降为0,达到最小值。
梯度向量:设是一个关于x的函数,其中x是向量变元,并且
则向量求导的梯度向量形式是:
如果假设是关于参数x的损失函数,则
就是损失函数的梯度向量,或者梯度表达式。
当损失函数的参数x选定一组取值之后,我们就能计算梯度表达式的取值,该值也被称为损失函数在某组参数取值下的梯度。
来个简单的数据集:
x | y |
1 | 2 |
2 | 4 |
3 | 6 |
使用简单线性回归对该组数据进行拟合
1、参数求解移动过程第一步
step1:随机选取初始参数值令
step2: 基于确定参数移动方向
梯度下降中,参数移动的目标是让损失函数取值减少,因此在上例中,由于w初始取值为10,所以应该沿着w减少的方向移动。
注意,如果损失函数包含多个参数的话,参数组变化方向就有多个,例如假设参数组包含两个参数w=(a,b),则w的变化就相当于是在(a, b)这个二维平面上的点发生移动。
损失函数的梯度向量表示形式为:
当时,梯度计算结果为
在梯度下降算法中,参数的移动方向是梯度的负方向。(10到224是增加的,则下一步我们要朝着数值减少的方向移动)
step3:确定移动步长并移动
步长或者学习率(Learning rate)lr = 0.01,则从进行移动的距离是
,而又是朝向梯度的负方向进行移动,因此
最终移动到了
2、梯度下降要多轮迭代
当从移动到
时
的计算公式如下:
二、梯度下降算法特性与学习率
定义一个函数来辅助进行本例中梯度下降的迭代:
def gd(lr=0.02,itera_times=20,w=10):
'''
梯度下降计算函数
:param lr: 学习率
:param itera_times: 迭代次数
:param w: 参数初始取值
:return: 每一轮迭代的参数计算结果列表
'''
results=[w]
for i in range(itera_times):
w-=lr*28*(w-2) #梯度计算公式
results.append(w)
return results
在远点迭代移动距离较大、近点迭代移动距离较小。
如果学习率过低,算法需要大量迭代才可收敛(20次迭代并不能到达最低点),耗时较长。如果学习率过高也可能越过最小值直接到达另一边,导致算法发散。
三、梯度下降一般建模流程与多维梯度下降
1、多维梯度下降
再来一个数据集:
数据特征 | 参数组 | 模型输出 | 数据标签 |
Whole weight(x) | (w,b) | Rings(y) | |
1 | (w,b) | w+b | 2 |
3 | (w,b) | 3w+b | 4 |
此时损失函数图像就是包含两个变量的三维图像。
2、梯度下降计算流程(手动)
step1:确定数据类型及模型
此处我们以y=wx+b简单线性回归对上述简单数据集进行拟合,参数向量包含两个分量。
step2:设置初始参数值
给初始参数赋予一组随机数作为初始取值。
np.random.seed(24)
w=np.random.randn(2,1)
step3:根据损失函数求出梯度表达式
线性回归的损失函数是围绕SSE及其衍生的指标所构建的损失函数,此处以MSE作为损失函数的构建指标,此处可借助已经定义好的SSELoss函数执行计算。
其中m为训练数据总数。
def SSELoss(X,w,y):
'''
SSE计算函数
:param X:输入数据的特征矩阵
:param w: 线性方程参数
:param y: 输入数据的标签数组
:return: 返回对于数据集预测结果和真实结果的误差平方和
'''
y_hat=X.dot(w)
SSE =(y-y_hat).T.dot(y-y_hat)
return SSE
def MSELoss(X,w,y):
'''
MSE指标计算函数
'''
SSE=SSELoss(X,w,y)
MSE=SSE/X.shape[0]
return MSE
使用SSE作为损失函数,则该损失函数的梯度表达式为:
钉一个矩阵求导的公式:
使用MSE作为损失函数,则该损失函数的梯度表达式为:
定义这个线性回归的梯度计算函数:
def lr_gd(X, w, y):
"""
线性回归梯度计算公式
"""
m = X.shape[0]
grad = 2 * X.T.dot((X.dot(w) - y)) / m #MSE
return grad
step4:执行梯度下降迭代
在梯度下降过程中,参数更新参照如下公式:
def w_cal(X, w, y, gd_cal, lr = 0.02, itera_times = 20):
"""
梯度下降中参数更新函数
:param X: 训练数据特征
:param w: 初始参数取值
:param y: 训练数据标签
:param gd_cal:梯度计算公式
:param lr: 学习率
:param itera_times: 迭代次数
:return w:最终参数计算结果
"""
for i in range(itera_times):
w -= lr * gd_cal(X, w, y)
return w
w = w_cal(features, w, labels, gd_cal = lr_gd, lr = 0.1, itera_times = 100)
在学习率为0.1的情况下,采用线性回归的梯度计算公式,迭代100轮。
四、二维梯度下降过程的可视化
1、等高线图
为了方便展示梯度下降算法的性质,通过绘制损失函数的等高线图来观察二维梯度下降过程中参数点移动的过程。
定义一个函数来记录每一次迭代的计算结果:
def w_cal_rec(X,w,y,gd_cal,lr=0.02,itera_times=20):
w_res=[np.copy(w)] #值的生拷贝
for i in range (itera_times):
w-=lr*gd_cal(X,w,y)
w_res.append(np.copy(w))
return w,w_res
绘制参数的移动轨迹:
np.random.seed(24)
w=np.random.randn(2,1)
w,w_res=w_cal_rec(features,w,labels,gd_cal=lr_gd,lr=0.1,itera_times=100)
print(w_res)
np.array(w_res)[:,0] #将w_res从list转化为array,再对二维的array进行索引
plt.plot(np.array(w_res)[:,0],np.array(w_res)[:,1],'-o',color='#ffD700')
plt.show()
通过等高线图来观察参数点逼近(1,1)点的移动情况。
x1,x2=np.meshgrid(np.arange(1,2,0.0005),np.arange(-1,1,0.0005)) #网格点坐标
plt.contour(x1,x2,(2-x1-x2)**2+(4-3*x1-x2)**2) #绘制等高线图
plt.plot(np.array(w_res)[:,0],np.array(w_res)[:,1],'-o',color='#ffD700')
plt.show()
2、损失函数取值变化图
随着梯度下降算法每一轮的迭代,我们需要考虑一下整体的损失值是否严格递减。
loss_value=np.array([MSELoss(features,np.array(w),labels)for w in w_res]).flatten()
print(loss_value)
plt.plot(np.arange(101),loss_value)
plt.show()
封装一下这个损失函数取值变化图,便于之后验证我们之后自己的损失值是否严格递减。
def loss_vis(X,w_res,y,loss_func):
loss_value=np.array([loss_func(X,np.array(w),y)for w in w_res]).flatten()
plt.plot(np.arange(len(loss_value)), loss_value)
五、梯度下降算法评价
1、优
- 相比大规模数值矩阵运算,梯度下降所遵循的迭代求解效率更高(尽管大规模矩阵运算也可以通过分块矩阵的划分来减少每一次计算的数据量)
- 对于某些最小二乘法无法计算全域唯一最优解的情况,梯度下降仍然能够有效进行最小值点(或者解空间)的寻找。
梯度下降——数值解
最小二乘法——解析解
2、缺
当损失函数不是严格意义上的凸函数时,也就是有可能存在局部最小值或者鞍点时,梯度下降并不一定能够准确找到全域最小值。
- 局部最小值点
参数最终停留在局部最小值附近,并且无法跨越该局部最小值点。
- 鞍点(saddle point)陷阱
鞍点是那些不是极值点但梯度为0的点。极值点:导数为0,两侧单调性不同。鞍点:导数为0单左右两边单调性相同,例如:
由损失函数到梯度计算表达式的过程,由于损失函数只有一个参数,所以梯度表达式为:
梯度下降无法跨越鞍点抵达更小值的点。
二维鞍点图像: