正向传播算法
设函数的输入数据为
x
x
x,参数为
w
w
w ,输出为
l
o
s
s
loss
loss。
正向传播过程如下:
y
^
=
x
∗
w
\hat{y} = x*w
y^=x∗w
s
=
y
−
y
^
s = y-\hat{y}
s=y−y^
l
o
s
s
=
s
2
loss = s^2
loss=s2
import torch
def forward(x, y, w):
# 其中 x,y 为输入数据,w为该函数所需要的参数
y_predicted = w * x
loss = (y_predicted - y)**2
return loss
# 测试代码
x = torch.tensor(1.0)
y = torch.tensor(2.0)
w = torch.tensor(1.0, requires_grad=True)
forward(x, y, w) # (2-1)²=1
# 输出结果
# tensor(1., grad_fn=<PowBackward0>)
反向传播算法
反向传播就是正向传播的反向计算。其实反向传播的目的就是计算输出值和参数之间的梯度关系。
利用反向传播求取函数关于权重的偏导(即梯度),然后根据偏导使用梯度下降算法找到最佳的参数。这个过程其实就是深度学习中模型训练的过程。
利用 loss.backward() 进行后向传播,求取所要可偏导变量的偏导值:
x = torch.tensor(1.0)
y = torch.tensor(2.0)
# 将需要求取的 w 设置为可偏导
w = torch.tensor(1.0, requires_grad=True)
loss = forward(x, y, w) # 计算损失
loss.backward() # 反向传播,计算梯度
print("此时,loss 关于 w 的偏导为:", w.grad)
w.grad.zero_() # 得到偏导后,清空梯度
# 输出结果
# 此时,loss 关于 w 的偏导为: tensor(-2.)
# tensor(0.)
梯度下降算法
假设 w 为损失函数需要求的变量,那么梯度下降算法的具体步骤如下:
- 随机初始化一个 w 的值。
- 在该 w 下进行正向传播,得到所有 x 的预测值 y_pre。
- 通过实际的值 y 和预测值 y_pre 计算损失。
- 通过损失计算梯度 dw。
- 更新 w : w = w − l r ⋅ d w w:w = w-lr\cdot dw w:w=w−lr⋅dw,其中 l r lr lr 为步长,可自定义具体的值。
- 重复步骤 2 − 5 2-5 2−5,直到损失降到较小位置。
人工实现
import numpy as np
# 所有点的预测值和实际值的距离的平方和,再取平均值(这种距离叫做欧氏距离)。
def loss(y, y_pred):
return ((y_pred - y)**2).mean()
#返回dJ/dw
def gradient(x, y, w):
return np.mean(2*w*x*x-2*x*y)
定义数据集和变量值:
# 正向传播,计算预测值
def forward(x):
return w * x
# 定义数据集合和 w 的初始化
X = np.array([1, 2, 3, 4], dtype=np.float32)
Y = np.array([2, 4, 6, 8], dtype=np.float32)
w = 0.0
# 定义步长和迭代次数
learning_rate = 0.01
n_iters = 20
利用梯度下降算法求解一元回归函数中的 w 的值:
for epoch in range(n_iters):
y_pred = forward(X)
#计算损失
l = loss(Y, y_pred)
#计算梯度
dw = gradient(X, Y, w)
#更新权重 w
w -= learning_rate * dw
if epoch % 2 == 0:
print(f'epoch {epoch+1}: w = {w:.3f}, loss = {l:.8f}')
print(f'根据训练模型预测,当 x =5 时,y 的值为: {forward(5):.3f}')
epoch 1: w = 0.300, loss = 30.00000000
epoch 3: w = 0.772, loss = 15.66018677
epoch 5: w = 1.113, loss = 8.17471600
epoch 7: w = 1.359, loss = 4.26725292
epoch 9: w = 1.537, loss = 2.22753215
epoch 11: w = 1.665, loss = 1.16278565
epoch 13: w = 1.758, loss = 0.60698175
epoch 15: w = 1.825, loss = 0.31684822
epoch 17: w = 1.874, loss = 0.16539653
epoch 19: w = 1.909, loss = 0.08633806
根据训练模型预测,当 x =5 时,y 的值为: 9.612
pytorch实现
模型的函数表达式非常复杂非常复杂时,手动定义该函数的梯度函数就变得非常困难了。因此,这里使用后向传播函数来实现梯度下降算法,求解最佳权重 w。
定义数据集合以及 w 的初始值,并将其设置为可以求偏导的张量:
import torch
X = torch.tensor([1, 2, 3, 4], dtype=torch.float32)
Y = torch.tensor([2, 4, 6, 8], dtype=torch.float32)
#初始化张量 w
w = torch.tensor(0.0, dtype=torch.float32, requires_grad=True)
# 定义步长和迭代次数
learning_rate = 0.01
n_iters = 20
接下来让我们使用 .backward()
直接求解梯度:
for epoch in range(n_iters):
y_pred = forward(X)
l = loss(Y, y_pred)
# 无需定义梯度求解的函数,直接求解梯度
l.backward()
# 利用梯度下降更新参数
with torch.no_grad():
# w.grad :返回 w 的梯度
w.data -= learning_rate * w.grad
# 清空梯度
w.grad.zero_()
if epoch % 2 == 0:
print(f'epoch {epoch+1}: w = {w.item():.3f}, loss = {l.item():.8f}')
print(f'根据训练模型预测,当 x =5 时,y 的值为: {forward(5):.3f}')
epoch 1: w = 0.300, loss = 30.00000000
epoch 3: w = 0.772, loss = 15.66018772
epoch 5: w = 1.113, loss = 8.17471695
epoch 7: w = 1.359, loss = 4.26725292
epoch 9: w = 1.537, loss = 2.22753215
epoch 11: w = 1.665, loss = 1.16278565
epoch 13: w = 1.758, loss = 0.60698116
epoch 15: w = 1.825, loss = 0.31684780
epoch 17: w = 1.874, loss = 0.16539653
epoch 19: w = 1.909, loss = 0.08633806
根据训练模型预测,当 x =5 时,y 的值为: 9.612
可以看到,利用 PyTorch 进行的梯度下降的结果和人工梯度下降结果一致。我们可以通过 PyTorch 中的 .backward()
,简洁明了的求取任何复杂函数的梯度,大大的节约了我们公式推导的时间。