说在前面:这部分分两块,一个是基于cost function的,也就是全数据集上的代价函数,另一个是从中随机抽取一个数据,基于loss function的,也就是损失函数,二者在forward、loss的函数构建、训练过程中的数据加载环节都有所区别,要注意区分,搞清搞透;
详细过程:
本课程的主要任务是构建第一个带有初始权重和训练过程的梯度下降模型:
导入numpy和matplotlib库;
导入数据 x_data 和 y_data;
定义前向传播函数:
forward:输出是预测值y_hat
定义代价函数:
cost:损失函数定义为MSE:均方根误差
此处要使用循环,把数据集中的数据对儿,一组一组地取出来,再做计算;
返回值需要归一化至单个数据的损失值;
定义梯度计算函数:
这个函数gradient很有意思,也不简单:需要根据反向传播,计算出代价函数对于权重的梯度,计算结果为grad += 2 * x * (x * w - y);
返回值同样需要归一化至单个数据的梯度;
创建两个空列表,因为后面绘图的时候要用:
分别是横轴的w_list和纵轴的mse_list
开始训练:
循环的次数epoch可以自定义:
首先调用代价函数cost计算数据集的代价值,注意,这里要传入所有数据,而不是再去用·zip·函数配合·for·循环每次取一个,因为在`cost·函数当中,已经写过单个数据对儿从数据集调出的过程;
再调用梯度计算函数grad,与上个函数相同;
核心来了——更新权重:
就是最经典的权重更新公式,用现有权重,减去学习率乘以梯度的乘积,每轮(epoch)训练都要循环更新;
此处尚不涉及权重清零的问题,这个问题后面也要详细记录,务必搞得非常透彻,因为它涉及到计算图的是否构建,以及tensor数据类型具有data和grad两个属性的问题(TBD);
随意打印想要看到的内容,一般是打印x_val、y_val、loss_val;
在循环中要把计算的结果,放进之前的空列表,用于绘图;
在获得了打印所需的数据列表只有,模式化地打印图像。
详细过程:
回头仔细看了看代码,其实区别并不大;
之前没理解的原因,不在于cost和loss的区别,而在于权重更新那里我没有懂透;
区别:
forward函数没有变化;
loss、gradient函数有变化,把cost当中,原本需要把数据对儿从数据集中取出的过程,全部转移到训练过程中,这个过程不是难点,不过刚动手写的时候,就会很困惑,尤其是别的地方也有困惑,就觉得难度很大,应付不过来;
梯度下降代码如下:
import numpy as np
import matplotlib.pyplot as plt
x_data = [1.0, 2.0, 3.0]
y_data = [2.0, 4.0, 6.0]
w = 1.0 # 初始权重的猜测
def forward(x): # 前馈计算
return x*w # 线性模型y_hat=x*w
def cost(xs, ys): # 计算MSE(平均平方误差)
cost = 0
for x, y in zip(xs, ys):
y_pre = forward(x)
cost += (y_pre - y) ** 2 # 平方后与cost求和
return cost/len(xs)
def gradient(xs, ys):
grad = 0
for x, y in zip(xs, ys):
grad += 2*x*(x*w-y)
return grad / len(xs)
epoch_list = [] # 权重
cost_list = [] # 对应权重的损失值
print('predict (before training)', 4, forward(4))
for epoch in range(100):
cost_val = cost(x_data, y_data)
grad_val = gradient(x_data, y_data)
w -= 0.01 * grad_val # 0.01为学习率,w=w - 学习率*梯度
print('Epoch:', epoch, 'w=', w, 'loss=', cost_val) # epoch为当前第几轮训练,w为当前权重,loss为当前损失值
epoch_list.append(epoch)
cost_list.append(cost_val)
print('Predict (after training)', 4, forward(4))
'''
w_list = [] # 权重
mse_list = [] # 对应权重的损失值
for w in np.arange(0.0, 4.1, 0.1): # w的取值是0.0---4.1,每间隔0.1取一个值
print('w=', w)
l_sum = 0
for x_val, y_val in zip(x_data, y_data):
y_pre_val = forward(x_val)
loss_val = loss(x_val, y_val)
l_sum += loss_val
print('\t', x_val, y_val, y_pre_val, loss_val)
print('MSE', l_sum/3)
w_list.append(w)
mse_list.append(l_sum/3)
'''
plt.plot(epoch_list, cost_list)
plt.ylabel('cost')
plt.xlabel('epoch')
plt.grid()
plt.show()
运行结果如下:
随机梯度下降代码如下:
"""随机梯度下降"""
import numpy as np
import matplotlib.pyplot as plt
x_data = [1.0, 2.0, 3.0]
y_data = [2.0, 4.0, 6.0]
w = 1.0 # 初始权重的猜测
def forward(x): # 前馈计算
return x*w # 线性模型y_hat=x*w
def loss(x, y): # 求一个样本的损失
y_pre = forward(x)
return (y_pre - y) ** 2
def gradient(x, y):
return 2 * x * (x * w - y) # 对损失函数loss求偏导
epoch_list = [] # 权重
cost_list = [] # 对应权重的损失值
print('predict (before training)', 4, forward(4))
for epoch in range(100): # 对每一个样本的梯度进行更新
for x, y in zip(x_data, y_data):
grad = gradient(x, y)
w -= 0.01 * grad # 0.01为学习率,w=w - 学习率*梯度
print('\tgrad: ', x, y, grad)
l = loss(x, y)
print('progress:', epoch, 'w=', w, 'loss=', l) # epoch为当前第几轮训练,w为当前权重,loss为当前损失值
epoch_list.append(epoch)
cost_list.append(l)
print('Predict (after training)', 4, forward(4))
plt.plot(epoch_list, cost_list)
plt.ylabel('cost')
plt.xlabel('epoch')
plt.grid()
plt.show()
运行结果如下:
视频讲解截图:
α为学习率,梯度下降使用了贪心算法的思想
梯度下降算法很难找到一个全局最优,一般都是局部最优
最难的问题是陷入鞍点
使用随机梯度下降(只取一个样本计算梯度)的原因:在陷入鞍点时,训练集里的随机噪声可能会把训练向前推动,朝着最优值前进。