线性回归
输入 输出
0.5 5.0
0.6 5.5
0.8 6.0
1.1 6.8
1.4 7.0
...
y=f(x) f(x)=kx+b
预测函数: y = w0+w1x
x : 输入
y : 输出
w0 w1 : 模型参数
所谓的模型训练, 就是根据已知的x与y, 找到最佳的模型参数w0 w1 , 使得尽可能精确的描述出输入和输出的关系.
5.0 = w0+w1 x 0.5
5.5 = w0+w1 x 0.6
单样本误差:
根据预测函数求出输入为x时的预测值: y’ = w0+w1x
单样本误差则为: 1/2 (y’ - y) 2
总样本误差:
把所有的单样本误差相加即为总样本误差:1/2 Σ (y’ - y) 2
损失函数:
loss = 1/2 Σ (w0 + w1x - y) 2
所以损失函数就是总样本误差关于模型参数w0 w1的函数, 该函数属于三维数学模型, 即需要找到一组w0 w1 使得loss取最小值.
案例: 实现线性回归模型梯度下降过程
先来分析一下代码,首先说一下训练模型的几个必备条件:
train_x = np.array([0.5, 0.6, 0.8, 1.1, 1.4])
train_y = np.array([5.0, 5.5, 6.0, 6.8, 7.0])
times = 1000 # 存储梯度下降的次数
rate = 0.01 # 记录每次梯度下降模型参数变化率
epoches = [] # 记录每次梯度下降的索引
w0, w1, losses = [1], [1], []
# 模拟梯度下降的过程
for i in range(1, times+1):
epoches.append(i)
loss = ((w0[-1] + w1[-1]*train_x \
- train_y) ** 2).sum() / 2
losses.append(loss)
# 把梯度下降的过程进行输出
print(
'{:4}> w0={:.8f}, w1={:.8f}, loss={:.8f}'.format(
epoches[-1], w0[-1],
w1[-1], losses[-1]))
# 根据偏导数公式, 求得w0与w1方向上的梯度值
d0=((w0[-1]+w1[-1]*train_x)-train_y).sum()
d1=(((w0[-1]+w1[-1]*train_x)-train_y)*\
train_x).sum()
w0.append(w0[-1] - rate*d0)
w1.append(w1[-1] - rate*d1)
# 经过1000次下降,
# 最终得到的w0与w1使得loss函数值接近最小
训练数据:因为线性函数只有一个x和一个y,那么输入就是x,输出就是y,所以train_x表示输入数据,train_y表示输出数据。
train_x = np.array([0.5, 0.6, 0.8, 1.1, 1.4])
train_y = np.array([5.0, 5.5, 6.0, 6.8, 7.0])
参数:这里的w0,w1就是线性模型的参数,也就是我们通过学习训练最终得到并保存的结果,如果是较大的模型,需要保存大量的参数。现在的模型可以看做是只有两个参数,因此在训练的过程中就要不断调整这两个参数来改变模型。怎么调整呢?那就要看学习率了。
学习率 rate:学习率决定了每次调整参数的多少,一般情况下学习率都不会太大,比如现在有一个参数是10,然后调整一次调整到9.99,这0.01就要通过学习率来调整,一般的调整不会是直接的加减乘除。
d0=((w0[-1]+w1[-1]*train_x)-train_y).sum()
w0.append(w0[-1] - rate*d0)
比如这两行代码,就是通过学习率来调整参数的,rate表示学习率,d0表示的是预测值与实际值的偏差。
损失函数 loss:损失函数的作用,主要是为了体现训练结果与实际数据的整体误差,
loss = ((w0[-1] + w1[-1]*train_x - train_y) ** 2).sum() / 2
这里是用二分之一的方差表示的损失函数,不管用什么样的函数,只要能够很好得体现预测值与实际值的偏差即可,而且不管是什么样的损失函数,一定是损失函数越小,训练的模型越准确。训练模型没问题的情况下,在训练的过程中是会保持梯度下降的。
训练次数:训练次数的作用就是通过学习率改变参数的次数。
观察一下这个小模型的训练结果。
如图:观察一下刚开始训练是的数据变换,w0和w1的初始值均为1,然后通过学习率调整,都明显的增大,损失函数刚开始的下降也十分明显。
然后观察后续的训练数据,训练到快要结束的时候,w0和w1的变化已经可以忽略不计,loss的已经下降到很小的范围,并且loss的变化也非常小,说明模型的训练还是比较成功的。
把训练的线性模型画出来看一下:可以看出训练好的线性模型基本上是能较好的表示出点的变化规律的。
pred_y = w0[-1] + w1[-1]*train_x
# 绘制训练数据
mp.figure('Linear Regression', facecolor='lightgray')
mp.title('Linear Regression', fontsize=16)
mp.xlabel('x', fontsize=12)
mp.ylabel('y', fontsize=12)
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.scatter(train_x, train_y, marker='o',
s = 60, c='dodgerblue',
label='Train Points')
mp.plot(train_x, pred_y, c='orangered',
linewidth=2, label='Regression Line')
mp.legend()
绘制随着每次梯度下降过程, w0 w1 loss函数的变化曲线,可以看出在刚开始训练时参数和损失函数的变化都比较明显,达到某一阈值后,变化明显减缓。
mp.figure('Training Progress', facecolor='lightgray')
mp.subplot(311)
mp.title('Training W0', fontsize=14)
mp.ylabel('w0', fontsize=12)
mp.grid(linestyle=':')
mp.tick_params(labelsize=10)
mp.plot(epoches, w0[:-1], c='dodgerblue',
label='w0 Progress')
mp.legend()
mp.subplot(312)
mp.title('Training W1', fontsize=14)
mp.ylabel('w1', fontsize=12)
mp.grid(linestyle=':')
mp.tick_params(labelsize=10)
mp.plot(epoches, w1[:-1], c='orangered',
label='w1 Progress')
mp.legend()
mp.subplot(313)
mp.title('Training Loss', fontsize=14)
mp.ylabel('loss', fontsize=12)
mp.grid(linestyle=':')
mp.tick_params(labelsize=10)
mp.plot(epoches, losses, c='deepskyblue',
label='loss Progress')
mp.legend()
在三维曲面中绘制梯度下降的过程,梯度下降的过程中,明显在某一个点位变化过多,冲过了最低点,然后后续通过模型自己调整,有逐渐回到了损失函数的最低点位置。
grid_w0, grid_w1 = np.meshgrid(
np.linspace(0, 9, 500),
np.linspace(0, 3.5, 500))
grid_loss = np.zeros_like(grid_w0)
for x, y in zip(train_x, train_y):
grid_loss+=(grid_w0+x*grid_w1 - y)**2 / 2
mp.figure('Loss Function')
ax3d = mp.gca(projection='3d')
ax3d.set_xlabel('w0', fontsize=14)
ax3d.set_ylabel('w1', fontsize=14)
ax3d.set_zlabel('loss', fontsize=14)
ax3d.plot_surface(grid_w0, grid_w1, grid_loss,
rstride=10, cstride=10, cmap='jet')
ax3d.plot(w0[:-1], w1[:-1], losses,
'o-', c='orangered', label='BGD', zorder=3)
如图,以等高线的方式绘制梯度下降的过程,可以更加明显的看出梯度下降的过程中参数变化的情况。
mp.figure('BGD Contour', facecolor='lightgray')
mp.title('BGD Contour', fontsize=16)
mp.xlabel('w0', fontsize=12)
mp.ylabel('w1', fontsize=12)
mp.tick_params(labelsize=10)
mp.contourf(grid_w0, grid_w1, grid_loss,
10, cmap='jet')
cntr = mp.contour(grid_w0, grid_w1, grid_loss,
10, color='black')
mp.clabel(cntr, inline_spacing=0.1, fmt='%.2f',
fontsize=8)
mp.plot(w0, w1, 'o-', c='red')
mp.tight_layout()
mp.show()
完整代码
"""
线性回归
实现线性回归模型梯度下降过程
"""
import numpy as np
import matplotlib.pyplot as mp
import mpl_toolkits.mplot3d as axes3d
train_x = np.array([0.5, 0.6, 0.8, 1.1, 1.4])
train_y = np.array([5.0, 5.5, 6.0, 6.8, 7.0])
times = 1000 # 存储梯度下降的次数
rate = 0.01 # 记录每次梯度下降模型参数变化率
epoches = [] # 记录每次梯度下降的索引
w0, w1, losses = [1], [1], []
# 模拟梯度下降的过程
for i in range(1, times+1):
epoches.append(i)
loss = ((w0[-1] + w1[-1]*train_x \
- train_y) ** 2).sum() / 2
losses.append(loss)
# 把梯度下降的过程进行输出
print(
'{:4}> w0={:.8f}, w1={:.8f}, loss={:.8f}'.format(
epoches[-1], w0[-1],
w1[-1], losses[-1]))
# 根据偏导数公式, 求得w0与w1方向上的梯度值
d0=((w0[-1]+w1[-1]*train_x)-train_y).sum()
d1=(((w0[-1]+w1[-1]*train_x)-train_y)*\
train_x).sum()
w0.append(w0[-1] - rate*d0)
w1.append(w1[-1] - rate*d1)
# 经过1000次下降,
# 最终得到的w0与w1使得loss函数值接近最小
pred_y = w0[-1] + w1[-1]*train_x
# 绘制训练数据
mp.figure('Linear Regression', facecolor='lightgray')
mp.title('Linear Regression', fontsize=16)
mp.xlabel('x', fontsize=12)
mp.ylabel('y', fontsize=12)
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.scatter(train_x, train_y, marker='o',
s = 60, c='dodgerblue',
label='Train Points')
mp.plot(train_x, pred_y, c='orangered',
linewidth=2, label='Regression Line')
mp.legend()
#绘制随着每次梯度下降过程, w0 w1 loss函数的变化曲线
mp.figure('Training Progress', facecolor='lightgray')
mp.subplot(311)
mp.title('Training W0', fontsize=14)
mp.ylabel('w0', fontsize=12)
mp.grid(linestyle=':')
mp.tick_params(labelsize=10)
mp.plot(epoches, w0[:-1], c='dodgerblue',
label='w0 Progress')
mp.legend()
mp.subplot(312)
mp.title('Training W1', fontsize=14)
mp.ylabel('w1', fontsize=12)
mp.grid(linestyle=':')
mp.tick_params(labelsize=10)
mp.plot(epoches, w1[:-1], c='orangered',
label='w1 Progress')
mp.legend()
mp.subplot(313)
mp.title('Training Loss', fontsize=14)
mp.ylabel('loss', fontsize=12)
mp.grid(linestyle=':')
mp.tick_params(labelsize=10)
mp.plot(epoches, losses, c='deepskyblue',
label='loss Progress')
mp.legend()
# 在三维曲面中绘制梯度下降的过程
grid_w0, grid_w1 = np.meshgrid(
np.linspace(0, 9, 500),
np.linspace(0, 3.5, 500))
grid_loss = np.zeros_like(grid_w0)
for x, y in zip(train_x, train_y):
grid_loss+=(grid_w0+x*grid_w1 - y)**2 / 2
mp.figure('Loss Function')
ax3d = mp.gca(projection='3d')
ax3d.set_xlabel('w0', fontsize=14)
ax3d.set_ylabel('w1', fontsize=14)
ax3d.set_zlabel('loss', fontsize=14)
ax3d.plot_surface(grid_w0, grid_w1, grid_loss,
rstride=10, cstride=10, cmap='jet')
ax3d.plot(w0[:-1], w1[:-1], losses,
'o-', c='orangered', label='BGD', zorder=3)
# 以等高线的方式绘制梯度下降的过程
mp.figure('BGD Contour', facecolor='lightgray')
mp.title('BGD Contour', fontsize=16)
mp.xlabel('w0', fontsize=12)
mp.ylabel('w1', fontsize=12)
mp.tick_params(labelsize=10)
mp.contourf(grid_w0, grid_w1, grid_loss,
10, cmap='jet')
cntr = mp.contour(grid_w0, grid_w1, grid_loss,
10, color='black')
mp.clabel(cntr, inline_spacing=0.1, fmt='%.2f',
fontsize=8)
mp.plot(w0, w1, 'o-', c='red')
mp.tight_layout()
mp.show()