其实就是个函数y=ω * x + b
通过不断调参来确定最优ω/b值从而确定最小损失:
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]
def forward(x):
return x * w
def loss(x, y):
y_pred = forward(x)
return (y_pred - y) * (y_pred - y)
w_list = []
mse_list = []
for w in np.arange(0.0, 4.1, 0.1):
print('w=', w)
l_sum = 0
for x_val, y_val in zip(x_data, y_data):
y_pred_val = forward(x_val)
loss_val = loss(x_val, y_val)
l_sum += loss_val
print('\t', x_val, y_val, y_pred_val, loss_val)
print('MSE=', l_sum / 3)
w_list.append(w)
mse_list.append(l_sum / 3)
画曲线图:
plt.plot(w_list, mse_list)
plt.ylabel('Loss')
plt.xlabel('w')
plt.show()
三、梯度下降法
就是通过求导后找优化的点来找到最优点:
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
def cost(xs, ys):
cost = 0
for x, y in zip(xs, ys):
y_pred = forward(x)
cost += (y_pred - y) ** 2
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)
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
print('Epoch:', epoch, 'w=', w, 'loss=', cost_val)
print('Predict (after training)', 4, forward(4))
在使用梯度下降时,有时候会因为遇到鞍点而无法找到全局最优点;
随机梯度下降是指从已有的N个cost中随机选取一个,即拿单个样本的损失来进行计算。这样就会有随机噪声,从而解决了鞍点问题;
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
def loss(x, y):
y_pred = forward(x)
return (y_pred - y) ** 2
#自己把目标函数求导之后把表达式写成函数
def gradient(x, y):
return 2 * x * (x * w - y)
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 = w - 0.01 * grad
print("\tgrad: ", x, y, grad)
l = loss(x, y)
print("progress:", epoch, "w=", w, "loss=", l)
print('Predict (after training)', 4, forward(4))
这样能避免鞍点:第一个遇到鞍点的值可以通过第二个点跨过
意思就是:梯度下降一条线(平均)都下降,随机梯度是随机一个点下降
可以看到,连续的性能低,但是时间快;单个的性能好,但是时间慢
因此,选择了折中的方法:batch方法,即每次多选几个单个样本来进行计算;
四、 后向传播
可以看出,对于线性神经网络的方程,可以通过展开将其变为一层。但是这并不符合深度学习的情况,所以就加了一个非线性的变化函数——激活函数;
就是先正向传播求解出最终的loss值/表达式,然后再反向传播来求导从而得出梯度(导函数值);loss的值可以保存下来用作可视化
在pytorch中进行正/反向计算
张量还保存了导数,因此用张量来进行计算比较好用;
import torch
x_data = [1.0, 2.0, 3.0]
y_data = [2.0, 4.0, 6.0]
#创建一个tensor变量,注意下面的格式
w = torch.Tensor([1.0])
#通过requires_grad来表明其需要计算梯度
w.requires_grad = True
#下面这两个函数实际上在构建计算图
def forward(x):
return x * w
def loss(x, y):
y_pred = forward(x)
return (y_pred - y) ** 2
print("predict (before training", 4, forward(4).item())
for epoch in range(100):
for x, y in zip(x_data, y_data):
#构建计算图
l = loss(x,y)
#只要backward后计算图就消失了
l.backward()
print('\tgrad:',x,y,w.grad.item())
#grad也是个张量,所以要取其data来进行计算
#在更新时不能使用d张量
w.data = w.data - 0.01 * w.grad.data
#通过backward计算的grad会被累计,所以在更新w之后要对其进行归零
w.grad.data.zero_()
print("progress:", epoch, l.item())
print("predict (after training)", 4, forward(4).item())
可以看到,损失值在一直减少,这也说明在越来越好
总之,反向传播的过程实际上就是:1.先算下loss;2.然后反向传播去算梯度;3.再用梯度下降方法来进行更新权重;