反向传播
两层的神经网络会有什么问题呢,如下图可以看到,两层的神经网络如果只进行线性操作的话,最后的表达式还是wx+b没有太大的变化,模型的复杂度还是很低
所以我们可以在每一层网络后边加非线性激活函数提高模型复杂度,这样得到的y就不是简单的线性堆叠了
复合函数链式法则可以从如下图中理解,可以看出层层嵌套,由外到里
反向传播例子,我们有个映射f=xw,输入x,w的值后,我们先对f关于x和w求偏导数得到他们的值,计算出预测的z值,再通过z值求损失L关于z的偏导数,再反向传播分别和f对x和f对w的偏导相乘即的损失L关于x和w的偏导,即可进行梯度下降
线性模型的计算图,反向传播是有个计算图的,如下图,依次去求偏导,可以看出只要包含参数w的值都需要求偏导
作业1:
作业2:
pytorch中的tensor张量,张量再pytorch动态图中是一个重要的成分元素,它包含了data和grad两个部分,如下图,data是个张量,grad是导数但是还包含动态图,因此梯度反向传播时要用w.grad.data值来更新梯度
来实际实操下载pytorch中进行反向传播;
首先定义张量w以及设置其需要反向传播计算梯度,也就是定义它为一个参数
然后,定义前馈传播和损失函数,这里w是张量,x会自动转化为张量形式,这样就会构建了一个计算图,如下图只要包含参数w的都需要计算梯度,计算图如下:
然后进行不同epoch的迭代参数更新找损失的最小值,下面用的是随机梯度下降方法,对每个样本进行梯度计算和损失计算不求平均。这里需要注意的是
1.首先梯度要进行反向传播也就是最后一个值,这里是损失函数值进行反向传播,利用.backward()函数即可
2.对于一个参数w来言,他有自己的输入值和梯度值,分别用w.data和w.grad.data来存储,w.grad存储的是计算图,不能用这个来进行梯度更新
3.最后要将每一次反向传播保存的梯度值进行清零,方便下一次运算重新开始不受影响
反向传播实践代码:
import torch
x_data = [1.0,2.0,3.0]
y_data = [2.0,4.0,6.0]
#定义张量w
w = torch.tensor([1.0])
#定义张量w需要计算梯度
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()) #这里要加item()是因为前向传播出来的是张量,这个就变成标量
for epoch in range(100):
for x,y in zip(x_data,y_data):
l = loss(x,y) #计算损失l,也是张量
l.backward() #l进行反向传播
print('\tgrad:',x,y,w.grad.item()) #注意grad包含计算图要想取梯度值需要用.data或者.item取一下
w.data = w.data -0.01 * w.grad.data #w.data也是张量
w.grad.data.zero_() #把刚才存储的梯度的值都变为0,方便下一次梯度更新
print('progress:',epoch,l.item())
print('predict after training',4,forward(4).item())
作业代码:
#作业
import numpy as np
import matplotlib.pyplot as plt
import torch
#先构建数据集
x_data = [0.0,1.0,2.0,3.0]
y_data = [4.0,9.0,18.0,31.0]
#创建空列表来存储损失w1,w2,b的值
w1_list = []
w2_list = []
b_list = []
epoch_list = np.arange(0,2000,1)
loss_list = []
epoch = 1
#定义初始的w1,w2,b的张量值
w1 = torch.tensor([10.0])
w2 = torch.tensor([10.0])
b = torch.tensor([10.0])
#定义其为参数可以进行梯度计算
w1.requires_grad = True
w2.requires_grad = True
b.requires_grad = True
#定义前向传播函数
def forward(x):
return w1*x**2+w2*x+b
#定义损失计算函数
def loss(y_pred,y):
return (y_pred-y)**2
#多次迭代前向传播
for epoch in range(500):
for x,y in zip(x_data,y_data):
y_pred = forward(x)
#构建图
l = loss(y_pred,y)
#反向传播l
l.backward()
#梯度更新
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_list.append(w1.data.item())
w2_list.append(w2.data.item())
b_list.append(b.data.item())
#epoch_list.append(epoch)
loss_list.append(l.item())
print('epoch:\t',epoch,'w1:\t',w1.data.item(),'w2:\t',w2.data.item(),'b:\t',b.data.item(),'loss\t',l.item())
#梯度清零
w1.grad.data.zero_()
w2.grad.data.zero_()
b.grad.data.zero_()
plt.plot(epoch_list,w1_list,'r',label='w1')
plt.plot(epoch_list,w2_list,'g',label='w2')
plt.plot(epoch_list,b_list,'y',label='b')
plt.xlabel('Epoch')
plt.show()