神经网络预测学习 梯度下降
神经网络知道了如何预测,也知道了自己预测的准不准。那么如何调整自己预测时的参数,就是神经网络学习的过程。
我们已经知道,预测得是否准确是由 和 决定的,所以神经网络学习的目的就是要找到合适的 和 。通过一个叫做梯度下降(gradient descent)的算法可以达到这个目的。梯度下降算法会一步一步地改变 和 的值,新的 和 会使损失函数的输出结果更小,即一步一步让预测更加精准。
如上图所示,损失函数J的形状是一个漏斗状。我们训练的目的就是找到在漏斗底部的一组 和 。这种漏斗状的函数被称为凸函数(向下凸起的函数)。
为了方便理解,上图只是个概念图。生成概念图代码如下
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
def J(w, b):
# 这里定义 J(w, b) 函数的形式
return w**2 + b**2
# 创建一个figure对象
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# 定义w和b的范围
w = np.linspace(-5, 5, 100)
b = np.linspace(-5, 5, 100)
# 使用numpy的meshgrid函数生成网格点
W, B = np.meshgrid(w, b)
# 计算J(w, b)的值
Z = J(W, B)
# 绘制曲面
surf = ax.plot_surface(W, B, Z, cmap='viridis', edgecolor='none')
# 设置坐标轴标签
ax.set_xlabel('w')
ax.set_ylabel('b')
ax.set_zlabel('J(w, b)')
plt.tick_params(labelbottom=False, labelleft=False)
# 显示图形
plt.show()
我们选择它为损失函数的原因正是因为它是一个凸函数。
为了方便理解,上图只是个概念图。生成概念图代码如下
import numpy as np
import matplotlib.pyplot as plt
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
def plot_loss_function():
# 创建数据点
w = np.linspace(-30, 30, num=200)
J = 0.01 * (w ** 2) # 损失函数的表达式
# 创建一个新的图形窗口
plt.figure(figsize=(8, 6))
# 在新的图形窗口中绘制曲线
plt.plot(w, J, label="损失函数", color='purple')
# 计算损失函数在每个点的斜率
def slope(w):
return 0.02 * w
# 绘制斜率三角形
def plot_slope_triangle(w_pos, J_pos, slope, color, label):
# 确定三角形的底边和高度
base = 2.5 # 三角形的底边长度
height = base * abs(slope) # 三角形的高度
# 确定三角形的坐标
x1 = w_pos
y1 = J_pos
x2 = w_pos - base if slope > 0 else w_pos + base
y2 = J_pos
x3 = w_pos
y3 = J_pos + height
# 绘制三角形
plt.fill([x1, x2, x3], [y1, y2, y3], color=color, alpha=0.5, label=label)
# 添加绿色三角形在(24.2, 6)位置
plt.scatter([24.2], [6], color='green', marker='^', s=100, label="w初始位置")
plot_slope_triangle(24.2, 0, slope(24.2), 'green', "初始斜率")
# 添加橙色三角形在(17.1, 2.92)位置
plt.scatter([17.1], [2.92], color='orange', marker='^', s=100, label="第一次梯度下降后")
plot_slope_triangle(17.1, 0, slope(17.1), 'orange', "第一次梯度下降后的斜率")
# 添加红色三角形在(10, 1)位置
plt.scatter([10], [1], color='red', marker='^', s=100, label="第二次梯度下降后")
plot_slope_triangle(10, 0, slope(10), 'red', "第二次梯度下降后的斜率")
# 添加最小值标记在(0, 0)位置
plt.scatter([0], [0], color='black', marker='o', s=100, label="损失函数的最小值")
# 添加红色三角形在(10, 1)位置
plt.scatter([-14.1], [2], color='blue', marker='^', s=100, label="步长过大的梯度下降后")
plot_slope_triangle(-14.1, 0, slope(-14.1), 'blue', "步长过大的梯度下降后")
# 添加网格线
plt.grid(linestyle='--', linewidth=0.5)
# 添加标签和标题
plt.xlabel('w')
plt.ylabel('J')
plt.title('损失函数曲线')
# 设置坐标轴范围
plt.xlim(-28, 28)
plt.ylim(-1, 10)
# 添加图例
plt.legend()
# 隐藏刻度
ax = plt.gca()
ax.axes.xaxis.set_ticklabels([])
ax.axes.yaxis.set_ticklabels([])
# 显示图形
plt.show()
if __name__ == "__main__":
plot_loss_function()
按图中所示(先看函数曲线的右半部分),可以看到每一个点的下方都对应了一个直角三角形。而随着梯度下降的进行,直角三角形的高(垂直方向边长)也逐渐减少。
直角三角形的斜边与函数曲线上的切线平行,那条切线的切点也就是直角三角形的对应点。
这里再引入一个数学知识:偏导数(直角三角形的斜率/变化比例)= = 直角三角形的高(垂直方向边长) / 底边(水平方向边长)
最直观的就是,直角三角型斜边越陡,斜率越大。
梯度下降算法会一步一步地更新 和 使损失函数的值一步一步地变得更小,最终找到损失函数的最小值或接近最小值的地方。
实际上的成本函数(针对训练集的损失函数) 是一个关于 权重 和 阈值 的函数。但是我们在这里假设 成本函数
只有一个参数 。(实际上的是一个向量或者是一组实数,我们在这里先假设他只是1个实数)
梯度下降算法通过改变 的值,让的坐标越来越接近 损失函数的最小值
梯度下降算法就是重复的执行上面的公式来不停更新的值。
- :是更新后的的值
- :更新前的的值,也就是待修改的,也是该公式的参数
- :学习率(learning rate),在这里面体现的是步长,也就是的值在当前变化中的改变量(除固定数值外,还存在
学习率衰减
和自适应学习率
)。在梯度下降里面,的改变,永远都是朝向损失函数的最小值
的。 - :是参数关于函数的偏导数。偏导数也可理解为是斜率,斜率则可理解为是变化比例。即改变后会按照多少比例改变。例如减少了2后,减少了6。那么的值就是3。。
不要忘记,我们为了做到精准的预测,要将 成本函数
的值降为最低或是接近最低。
那么也就可以发现,随着的值越来越接近 损失函数的最小值
时,直角三角形的高在减少。斜率(变化率)也在减少。变化率减少意味着再改变时,对的改变也越来越小。
我们按照这个逐渐减少的变化率让不断变化,那么相应的也会不断的接近 损失函数的最小值
如此一来,我们就能找到当是最小值时,的值是多少,从而达到最终目的。
梯度下降基本思想
梯度下降的基本思想是沿着目标函数梯度的负方向更新参数,因为梯度指向的是函数值增加最快的方向,而我们希望找到的是最小值点,因此要沿其相反方向移动。
关于学习率r
学习率(通常用 或 表示)是梯度下降算法中的一个重要超参数,它决定了参数更新的步长大小。选择合适的学习率对于算法的收敛速度和最终收敛到的解的质量至关重要。
最简单的方法是手动设置一个固定的学习率。这通常是一个正的小数值,例如 0.01 或 0.001。然而,这种方法可能会导致以下问题:
- 如果学习率设置得过小,算法收敛会非常慢。
- 如果学习率设置得过大,则可能导致算法在最优解附近震荡甚至发散。
除固定学习率外,还有其他很多方式确定学习率,例如:网格搜索、随机搜索、学习率衰减、自适应学习率方法、Learning Rate Scheduling以及Learning Rate Finder等
关于参数w的减少
全程一直没有提到“减少”这两个字,就是怕看到这篇文章的人混淆。
从上面提到的梯度下降公式来看,它确确实实是从原的基础上做了减法运算,然后获取到的新
我举的例子中,由于是在函数的右半部分往回走。说的值在减少,相信大家没有问题。
而如果说,的初始位置在函数的左半部分,各位知道减法减去一个负数,实际上在做加法运算。但是大家数学不好,可能就不知道怎么就变成负数了。
斜率也是有正负之分的。
如果随着函数的横坐标增加,纵坐标的值也在增加,那这个斜率就是正斜率。体现出来的就是右上角与左下角的直线。
如果随着函数的横坐标增加,纵坐标的值在减少,那么这个斜率就是负斜率。体现出来的就是左上角与右下角的直线。
大家可以看一下我在上面损失函数曲线的图中画出来的左半部分的蓝色三角型,它的斜边方向与我说的负斜率的情况完全吻合。
所以负负得正,这个减法运算,把实际的参数代入后,就变成了加法运算