反向传播
反向传播是在神经网络中一种非常重要的算法
由于在实际的神经网络中,层数较多,梯度计算比较复杂,不可能每一步都要推出来梯度的公式,此时就要利用链式法则,通过反向传播,来计算梯度,更新参数
以两层神经网络为例,其最终的输出就是
y^=W2(W1X+b1)+b2
\hat y = W_2(W_1X + b_1) + b_2
y^=W2(W1X+b1)+b2
其计算图如下所示:
激活函数
对于线性模型,无论其增加多少层,最终都能化简为线性模型,即对于线性模型而言,增加层数对模型没有意义
以两层线性模型而言,
y^=W2(W1X+b1)+b2=W2W1X+(W2b1+b2)=WX+b
\hat y = W_2(W_1X +b_1) + b_2
= W_2W_1X+(W_2b_1 + b_2)
=WX + b
y^=W2(W1X+b1)+b2=W2W1X+(W2b1+b2)=WX+b
因此,为了解决此问题,我们要在每一层的输出中添加一个非线性变化函数,即激活函数,对输出中张量的每一个值都非线性化,使得模型更加准确
反向传播过程
首先生成计算图
然后进行前馈计算 Forward
,即沿着计算图边的方向进行计算,由输入求得losslossloss
同时在运算的过程中还要计算输出zzz关于xxx和www的导数,即∂z∂x\frac{\partial z}{\partial x }∂x∂z和∂z∂w\frac{\partial z}{\partial w}∂w∂z
{再由损失losslossloss往回算,进行反向传播,计算∂L∂z\frac{\partial L}{\partial z}∂z∂L,通过链式法则,计算出梯度,即$\frac{\partial L}{\partial x} = \frac{\partial L}{\partial z}\frac{\partial z}{\partial x} 和和和\frac{\partial L}{\partial w} = \frac{\partial L}{\partial z}\frac{\partial z}{\partial w} $
最后再通过梯度进行参数更新
以线性模型为例,整体流程如下图所示
Pytorch中使用前馈和反向传播
张量
在Pytorch
中,常用tensor
保存这种数值
tensor
是一个类,通常保存两种类型的数:
data
grad
import torch # 导入Pytorch
## 数据准备
x_data = [1.0,2.0,3.0]
y_data = [2.0,4.0,6.0]
w = torch.tensor([1.0]) # 创建权重
w.requires_grad = True # 标明需要计算梯度
其中:
torch.tensor()
用法和np.array()
用法一致- 要显示标明需要计算梯度,即
w.requires_grad = True
def forward(x):
return x*w # x会自动转化为tensor类型
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) # 前馈计算损失
l.backward() # 反向传播
print("\tgrad:", x, y, w.grad.item()) # .item()将tensor转化为标量 即拿出具体数值
w.data = w.data - 0.01*w.grad.data # 具体计算时要使用.data
w.grad.zero_() # 梯度清零 !!!
print("progress:", epoch, l.item())
cost_history.append(l.item())
print("predict (after training)", 4, forward(4).item())
注意:
- 每一步更新梯度时,要使用
.data
来更新,这样不会影响计算图的改变 .item()
和.data
有区别.item()
是把tensor
类型中的数值拿出来,最终是一个标量数值.data
返回的仍然是tensor
类型,使用,data
进行运算时,不会构建相应的计算图
作业
import torch
x_data = [1.0,2.0,3.0]
y_data = [2.0,4.0,6.0]
w1 = torch.tensor([1.0])
w1.requires_grad = True
w2 = torch.tensor([1.0])
w2.requires_grad = True
b = torch.tensor([1.0])
b.requires_grad = True
def forward(x):
return w1*x**2 + w2*x + b
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)
l.backward()
print("\tgrad", x, y, w1.grad.item(), w2.grad.item(), b.grad.item())
w1.data = w1.data - 0.01 * w1.grad.data
w2.data = w2.data - 0.01 * w2.grad.data
b.data = b.data - 0.01 * b.grad.data
w1.grad.data.zero_()
w2.grad.data.zero_()
b.grad.data.zero_()
print("progress", epoch, l.item())
print("predict (before training)", 4, forward(4).item())
print("w1=",w1.item(), "\tw2=", w2.item(), "\tb=",b.item())