我们要学习一个机器学习的重要方法——梯度下降法(Gradient Descent)梯度下降法并不是一个机器学习的算法,它既不能解决回归问题,也不能解决分类问题。
那么它是什么呢?梯度下降是一种基于搜索的最优化的方法,它的目标是用于优化一个目标函数。
【机器学习|梯度下降法】在机器学习中,梯度下降法的作用就是:最小化一个损失函数。
梯度下降法的模拟与可视化
文章图片
上图描述的是一个损失函数在函数不同参数取值的情况下(x轴),对应变化值(y轴)。
对于损失函数,应该有一个最小值。对于最小化损失函数这样一个目标,实际上就是在上图所示的坐标系中,寻找合适的参数,使得损失函数的取值最小。
在这个例子是在2维平面空间上的演示,所以参数只有1个(x轴的取值)。意味着每个参数,都对应着一个损失函数值。实际上模型的参数往往不止一个,所以这个图像描述的只是损失函数优化过程的一种简单表达。
我们用代码来绘制一张这样的图
import numpy as np
import matplotlib.pyplot as plt
# 创建等差数列x,代表模型的参数取值,共150个
plot_x = np.linspace(-1., 6., 150)
print(plot_x)
[-1. -0.95302013 -0.90604027 -0.8590604 -0.81208054 -0.76510067 -0.71812081 -0.67114094 -0.62416107 -0.57718121 -0.53020134 -0.48322148 -0.43624161 -0.38926174 -0.34228188 -0.29530201 -0.24832215 -0.20134228 …… 略 …… 5.20134228 5.24832215 5.29530201 5.34228188 5.38926174 5.43624161 5.48322148 5.53020134 5.57718121 5.62416107 5.67114094 5.71812081 5.76510067 5.81208054 5.8590604 5.90604027 5.95302013 6. ]
# 通过二次方程来模拟一个损失函数的计算,plot_y值就是图形上弧线所对应的点
plot_y = (plot_x-2.5)**2 - 1
绘制图形
plt.plot(plot_x, plot_y)
plt.show()
文章图片
下面,我们来尝试一下实现我们的梯度下降法。
首先定义一个函数,来计算损失函数对应的导数。目标就是计算参数theat的导数
def derivative(theta):
return 2*(theta-2.5)
还需要定义一个函数来计算theat值对应的损失函数
def loss(theta):
return (theta-2.5)**2 - 1
计算梯度值
# 以0作为theta的初始点
theta = 0.
eta = 0.1
epsilon = 1e-8
while True:
# 计算当前theta对应点的梯度(导数)
gradient = derivative(theta) # 更新theta前,先积累下上一次的theta值
last_theta = theta# 更新theta,向导数的负方向移动一步,步长使用eta(学习率)来控制
theta = theta - eta * gradient # 理论上theta最小值应当为0是最佳。但实际情况下,theta很难达到刚好等于0的情况
# 所以我们可以设置一个最小值spsilon来表示我们的需要theta达到的最小值目标
# 判断theta每次更新后,和上一个theta的差值是否已满足小于epsilon(最小值)的条件
# 满足的话就终止运算
if(abs(loss(theta) - loss(last_theta)) < epsilon):
breakprint(theta)
print(loss(theta))
2.499891109642585 -0.99999998814289
通过结果可以看出,当theta取2.5时候,损失函数的最小值正好对应着截距-1
学习率对梯度的影响
为了能够计算不同学习率下,theta的每一步变更值。我们对代码进行一些补充。
theta = 0.0
eta = 0.1
epsilon = 1e-8# 添加一个记录每一步theta变更的list
theta_history = [theta]
while True:
gradient = derivative(theta)
last_theta = theta
theta = theta - eta * gradient
# 更新theta后记录它们的值
theta_history.append(theta)if(abs(loss(theta) - loss(last_theta)) < epsilon):
break
绘制theta每一步的变更
plt.plot(plot_x, loss(plot_x))
plt.plot(np.array(theta_history), loss(np.array(theta_history)), color="r", marker='+')
plt.show()
文章图片
可以看到theta在下降过程中,每一步都在逐渐变小(逐渐逼近),直到满足小于epsilon的条件。
查看一下theta_history的长度
len(theta_history)
46
我们通过梯度下降法,经过了45次查找,最终找到了theta的最小值。
为了更方便的调试eta(学习率)的值,我们对代码进行进一步的封装。把梯度下降方法和绘图分别封装为两个函数。
theta_history = []def gradient_descent(initial_theta, eta, epsilon=1e-8):
theta = initial_theta
theta_history.append(initial_theta)while True:
gradient = derivative(theta)
last_theta = theta
theta = theta - eta * gradient
theta_history.append(theta)if(abs(loss(theta) - loss(last_theta)) < epsilon):
breakdef plot_theta_history():
plt.plot(plot_x, loss(plot_x))
plt.plot(np.array(theta_history),loss(np.array(theta_history)), color="r", marker='+')
plt.show()
尝试更小的eta值
eta = 0.01
theta_history = []
gradient_descent(0, eta)
plot_theta_history()
文章图片
theta下降的路径中,每一步的长度更短了,步数也更多了。
len(theta_history)
424
这次经过了424步才得到theta的最小值。
eta就是梯度下降中我们常常谈到的学习率learn rate(lr)。学习率的大小直接影响到梯度更新的步数。很明显,学习率越小,theta下降的步长越小。所需要的步数(计算次数)也就越多。
试试更小的学习率
eta = 0.001
theta_history = []
gradient_descent(0, eta)
plot_theta_history()
文章图片
步长已经短到几乎连成一条粗线
len(theta_history)
3682
共经过了3682步
那么学习率调大一些是不是更好呢?
eta = 0.8
theta_history = []
gradient_descent(0, eta)
plot_theta_history()
文章图片
theta变成了从曲线的左侧跳到了右侧,由于eta还是够小。最终我们还是计算得到了theta的最小值。
如果更大一些的学习率,会是什么效果呢?
eta = 1.1
theta_history = []
gradient_descent(0, eta)
文章图片
直接报错!
错误描述信息是:结果值太大了。
为什么会产生这样的原因呢?
原因是eta太大所导致的,theta在每次更新也会变得越来越大。梯度非但没有收敛,反而在向着反方向狂奔。直到结果值最终超出了计算机所能容纳的最大值,错误类型OverflowError(计算溢出错误)。
为避免计算值的溢出,我们改造损失值计算方法
def loss(theta):
try:
return (theta-2.5)**2 - 1.
except:
return float('inf') # 计算溢出时,直接返回一个float的最大值
给梯度更新方法添加一个最大循环次数
def gradient_descent(initial_theta, eta, n_iters = 1e4, epsilon=1e-8):theta = initial_theta
i_iter = 0 # 初始循环次数
theta_history.append(initial_theta)while i_iter < n_iters: # 小于最大循环次数
gradient = derivative(theta)
last_theta = theta
theta = theta - eta * gradient
theta_history.append(theta)if(abs(loss(theta) - loss(last_theta)) < epsilon):
breaki_iter += 1# 循环次数+1
重新执行刚刚会报错的
eta = 1.1
theta_history = []
gradient_descent(0, eta)
没有错误了~
len(theta_history)
10001
达到最大循环次数后,梯度计算就停止了。
为了观察eta=1.1时,theta到底是如何更新的。我们指定一个有限的梯度更新次数(10次)
eta = 1.1
theta_history = []
gradient_descent(0, eta, n_iters=10)
plot_theta_history()
文章图片
theta的值从曲线出发,逐渐向外。最终越变越大……
学习率的最佳取值
学习率eta的取值,是不是1就是极限值呢?
实际上eta的取值是和损失函数相关的,或者说时和theta的导数相关的。所以没有一个固定标准。
所以,学习率对于梯度下降法来说也是一个超参数。也需要网格搜索来寻找。
保险的方法,是把eta先设置为0.01,然后逐渐寻找它的最佳取值。大多数函数,都是可以胜任的。
如果出现了参数值过大,也可以用上面的方法绘制图形来查看哦~
推荐阅读
- 网络通信安全及科学技术专栏|基于PMC指数模型的电力政策量化及其在负荷预测中的应用
- c++|为什么每一名程序员都应该学习 C++()
- 神经网络|只需两行代码,2080Ti 就能当 V100用,这个炼丹神器真牛!
- 自然语言处理|横扫六大权威榜单后,达摩院开源深度语言模型体系 AliceMind
- 医学影像|Pair 智能标注神器,医生的好帮手
- 大数据|Pair智能标注神器,全面升级,更加智能
- 资讯|AI 编程“神器”国产化!华为耗时 8 个月,这个能用中文生成代码的模型诞生了...
- 药老算法|深入理解搜索引擎——详解query理解
- 自然语言处理|医疗健康领域的短文本解析探索----文本纠错