B站视频链接:04.反向传播_哔哩哔哩_bilibili
写在前面:看完梯度下降和反向传播两个视频,我的理解是他们的思想都是梯度下降算法,仅仅是通过反向传播这个方法能够更简易的求出损失函数对权重的导数而已,优化了计算方式,不需要靠人脑去写解析式求导了。
梯度下降法计算的不足
梯度下降中的计算方法:求导写解析式
神经网络中的表现形式
更新w,计算损失对w的导数
局限性:当遇到复杂的神经网络时,权重很多,计算复杂
此时把神经网络看成一个图,在图上传播梯度,最终可以根据链式法则求出loss对w的权重,该算法就叫反向传播back propagation
反向传播
计算图
MM:matrix multiplication 矩阵乘法
ADD:vector addition 向量加法
bias:偏差
权重w维度m*n或者n*m都可以,只是加不加转置的问题。
一个两层的神经网络
矩阵计算推荐该书,名字很有趣哇:矩阵食谱。
matrix-cook-book.pdf (pku.edu.cn)
激活函数
但是通过展开可以发现,不管用多少层,最终都可以写成一层的形式,所以需要引入激活函数
在每一层后都加入一个非线性变换函数,
补充一个sigmoid函数: Sigmoid函数解析__Sumor的博客-CSDN博客
chain rule 链式法则
复合函数的求导
步骤1:创建计算图(前馈)
先前馈,算出loss,再用链式法则反推
举例说明
把要训练的值显示为蓝色,整个反向传播的计算过程如下:
加上偏置项后:
PyTorch中的反向传播
tensor实际上是个类,它可以存标量、向量、矩阵、高阶矩阵。。
它有两个重要的成员:
data:保存权重本身的值,类型tensor,被取时不会建立计算图
grad:保存损失函数对权重的导数,类型tensor,被取时会建立计算图
所以在使用pytorch时,实际就在构建上文的计算图
代码分析
如果需要autograd机制,张量的元素变量requires_grad必须设置为True。
由于w是tensor,x其实做了类型的自动转换,转换为tensor后再进行数乘,最后返回的值的类型也是tensor。
由于w需要计算梯度,所以输出的y_hat也需要梯度。
每调用一次loss函数,计算图就被动态的构建出来。
前馈:计算出loss,即l,
反馈:调用这个张量的成员函数backward。
由于一开始设置的张量是w,会把求出的梯度存到w中,一旦调用backward后,这个计算图就会被释放。下一次再调用时会创建新的计算图。不保留是由于每次的计算图可能会有差异
获得梯度数值需要通过w.grad.data;如果用w.grad还是在建立计算图
tensor.item() :直接把数值拿出来,变成python中的标量
构建模型时直接使用的tensor类型(构建计算图),但在更新权重公式时需要使用data
当涉及到求和时,如果写的是sum+=l,由于l是张量,其实一直在构建计算图,内存会爆。应该改成
sum+=l.item()
每次都需要把权重的梯度里的数据都清零,否则w.grad中的数据会累加。
在其他模型设计的过程中,有可能需要梯度的累加。
代码例子
参考:PyTorch 深度学习实践 第4讲_错错莫的博客-CSDN博客
import torch
x_data = [1.0, 2.0, 3.0]
y_data = [2.0, 4.0, 6.0]
w = torch.tensor([1.0]) # w的初值为1.0
w.requires_grad = True # 需要计算梯度
def forward(x):
return x*w # w是一个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是一个张量,tensor主要是在建立计算图 forward, compute the loss
l.backward() # backward,compute grad for Tensor whose requires_grad set to True
print('\tgrad:', x, y, w.data, w.grad.item())
w.data = w.data - 0.01 * w.grad.data # 权重更新时,注意grad也是一个tensor
w.grad.data.zero_() # after update, remember set the grad to zero
print('progress:', epoch, l.item()) # 取出loss使用l.item,不要直接使用l(l是tensor会构建计算图)
print("predict (after training)", 4, forward(4).item())
作业
计算图
代码
import torch
import matplotlib.pyplot as plt
# quadratic model y^=w1x^2 + w2x + b
x_data = [1.0, 2.0, 3.0]
y_data = [2.0, 4.0, 6.0]
w1 = torch.tensor([1.0]) # w1的初值为1.0
w1.requires_grad = True # 需要计算梯度
w2 = torch.tensor([1.0]) # w2的初值为1.0
w2.requires_grad = True # 需要计算梯度
b = torch.tensor([1.0]) # b的初值为1.0
b.requires_grad = True # 需要计算梯度
def forward(x):
return w1*x*x + w2*x + b # w是一个Tensor
def loss(x, y):
y_pred = forward(x)
return (y_pred - y)**2
print("predict (before training)", 4, forward(4).item())
epoch_list = [] # 存起来好画图
cost_list = []
w1_list = []
w2_list = []
b_list = []
for epoch in range(5000):
for x, y in zip(x_data, y_data):
l =loss(x,y) # l是一个张量,tensor主要是在建立计算图 forward, compute the loss
l.backward() # backward,compute grad for Tensor whose requires_grad set to True
print('\tgrad:', x, y, w1.data, w2.data, b.data)
w1.data = w1.data - 0.01 * w1.grad.data # 权重更新时,注意grad也是一个tensor
w2.data = w2.data - 0.01 * w2.grad.data
b.data = b.data - 0.01 * b.grad.data
w1.grad.data.zero_() # after update, remember set the grad to zero
w2.grad.data.zero_()
b.grad.data.zero_()
print('progress:', epoch, l.item()) # 取出loss使用l.item,不要直接使用l(l是tensor会构建计算图)
epoch_list.append(epoch)
cost_list.append(l.item())
w1_list.append(w1.item())
w2_list.append(w2.item())
b_list.append(b.item())
print("predict (after training)", 4, forward(4).item())
plt.figure(1)
plt.plot(epoch_list,cost_list)#画在图1上
plt.ylabel('cost')
plt.xlabel('epoch')
plt.show()
plt.figure(2)
plt.plot(epoch_list,w1_list, "g", label="w1")#画在图2上,且不在一个窗口
plt.plot(epoch_list,w2_list, "r", label="w2")
plt.plot(epoch_list,b_list, label="b")
plt.legend()
plt.xlabel("epoch")
plt.show()
#两张图一起show
# plt.figure(1)
# plt.subplot(1, 2, 1) #图一包含1行2列子图,当前画在第一行第一列图上
# plt.plot(epoch_list,cost_list)
# plt.ylabel('cost')
# plt.xlabel('epoch')
# plt.figure(1)
# plt.subplot(1, 2, 2)#当前画在第一行第2列图上
# plt.plot(epoch_list,w1_list, "g", label="w1")
# plt.plot(epoch_list,w2_list, "r", label="w2")
# plt.plot(epoch_list,b_list, label="b")
# plt.legend()
# plt.xlabel("epoch")
# plt.show() #两张图一起show
代码参考:
PyTorch学习(三)--反向传播_陈同学爱吃方便面的博客-CSDN博客
构造BP网络使用反向传播拟合线性、非线性模型_牛客博客 (nowcoder.net)
有一个疑问,这三个同时先后更新不会有耦合吗?epoch5000次才得到合适的答案是不是就说明它们之间有耦合。。。