3 反向传播
3 反向传播
B站视频学习链接:3 反向传播
文章目录
- 3 反向传播
- 0 复习回顾
- 1 反向传播原理
- 1.1 引入原理
- 1.3 求导法则
- 1.4 求导计算图
- 2 代码使用
- 2.1 分块编写
- 2.2 代码综合
- 3 课后作业
- 3.1 换一组变量进行推导并绘制其计算图
- 3.2 增加模型复杂度继续进行`pytorch`求解
- 3.3 尝试使用**二次函数模型**进行**反向传播**的求解
0 复习回顾
在上一节课程中,主要使用的是numpy
库函数配合梯度下降算法来进行咱们权重的一个选取操作。
主要的模型是一个最简单的线性模型*(是线性模型里面最简单的一个哈)*,y^=x∗ω\hat{y}=x*\omegay^=x∗ω,用这个模型进行的一个算法的模拟。就像是下面这个图片展示的这个样子。
**第一步、**咱们输入了变量xxx和相对应的权重www,并将这两个变量按照函数要求进行数值相乘,得到咱们的计算值y^\hat{y}y^。
第二步、将得到的计算值y^\hat{y}y^作为输入重新进入下一层内求出损失函数losslossloss。
**第三步、**求解该损失函数在该条件下的梯度,∂lossn∂ω=2⋅xn⋅(xn⋅ω−yn)\frac{\partial loss_n}{\partial\omega}=2\cdot x_n\cdot(x_n\cdot\omega-y_n)∂ω∂lossn=2⋅xn⋅(xn⋅ω−yn)。
**第四步、**重新更新咱们的权重,ω=ω−α∂loss∂ω\omega=\omega-\alpha\frac{\partial loss}{\partial\omega}ω=ω−α∂ω∂loss,知道稳定收敛为止。
咱们上一次的例子是一个线性的模型,同时还是线性模型里面最简单的一个模型,因此在计算解析式的时候难度不大,但是一般而言,咱们的深度学习网络是有很多层的,并且输入的变量个数也不仅仅只有一个,像是下面这图片所示。
试问这种类型的深度学习网络模型该如何求解他的解析式呢?
这个模型左侧有555组xxx的输入,第一个隐层有666个节点,因此第一层就有303030个权重,那么后面慢慢累积起来,这个运算量大的恐怖,基本上是无法求解他的解析式的!
那么我们应该如何求解他的梯度呢,这个时候咱们想到了链式法则。
1 反向传播原理
1.1 引入原理
首先让我们上一点难度,看看双层的线性模型是个什么样子的,下面是一个双层的神经网络模型y^=W2(W1⋅X+b1)+b2\hat{y}=W_{2}(W_{1}\cdot X+b_{1})+b_{2}y^=W2(W1⋅X+b1)+b2。
这个是一个双层的神经网络,首先咱们看看他的第一层,也就是权重w1w_1w1所在的那一层
在这里面我们令H(1)H^{(1)}H(1)作为我们的隐层则有H(1)=W1TXH^{(1)} = W_1^{T}XH(1)=W1TX。这几个数据的形状为
因此第一层是这个玩意儿,我们需要确定矩阵的形状大小,一定要求这W1W_1W1的大小是m×nm \times nm×n的其中mmmmmm表示的是matrixmultiplicationmatrix multiplicationmatrixmultiplication 每一个隐层都有一个偏置量即bias−bbias-bbias−b,b1b_1b1是mmm维度的
然后我们将第一层的输出结果进行再一次的矩阵乘法,便得到了第二层的神经网络。
而对于矩阵求导的话可以找一本书叫做mairixcookbookmairix\ cookbookmairix cookbook这本书
本地位置: [martix cookbook.pdf](…\martix cookbook.pdf)
网络连接:martix-cookbook-pdf
j如果咱们现在将上面讲的双层线性模型进行展开可以得到下面这个东西,不难看出多层线性变换可以获得同一个形式的解,因此这么多层就没有意义了,故需要添加非线性的层才有意义。
y^=W2(W1⋅X+b1)+b2=W2⋅W1⋅X+(W2b1+b2)=W⋅X+b\begin{aligned}&\hat{y}=W_{2}(W_{1}\cdot X+b_{1})+b_{2}\\&=W_{2}\cdot W_{1}\cdot X+(W_{2}b_{1}+b_{2})\\&=W\cdot X+b\end{aligned} y^=W2(W1⋅X+b1)+b2=W2⋅W1⋅X+(W2b1+b2)=W⋅X+b
就像下面一样我们可以适当的引入一些非线性层会更加的有意义。
上面就是采用了一些非线性的函数进行了优化以防止展开成基本相同的线性函数。
1.3 求导法则
上面是一个比较好的一个概念,其中g,fg , fg,f这两个分别是两种对应法则,fog(x)fog(x)fog(x)就是f(g(x))f(g(x))f(g(x))因此,如果相对x求导数,那么就需要使用矩阵的链式法则。
dfog(x)dx=dfog(x)dg(x)×dg(x)dx\frac{\mathrm{d}fog(x)}{\mathrm{d}x}=\frac{\mathrm{d}fog(x)}{\mathrm{d}g(x)}\times\frac{\mathrm{d}g(x)}{\mathrm{d}x}dxdfog(x)=dg(x)dfog(x)×dxdg(x)
最主要的就是这个链式法则在矩阵上面的应用。
1.4 求导计算图
现在咱们同样对一个最简单的模型y^=x×ω\hat{y}=x\times \omegay^=x×ω进行讲解。
**前馈;**输入参数x,wx,wx,w我们将这两个参数传入到模型f(x)f(x)f(x)中,便可以得到第一个函数z=f(x,w)z= f(x,w)z=f(x,w),这个时候的zzz实际上就是所谓的虚拟计算值,将这个数值和真实值一起边可求得损失函数loss()loss()loss(),有了损失函数便可以使用梯度下降算法进行权重的求解啦。
**反馈(反向传播);**反向和正向在说法上就刚好相反,首先我们得到了损失函数关于传入参数zzz的导数∂z∂ω\frac{\partial z}{\partial \omega}∂ω∂z这时候我们拟用链式求导法则,通过∂L∂x=∂L∂z⋅∂z∂x\frac{\partial L}{\partial x}=\frac{\partial L}{\partial z}\cdot\frac{\partial z}{\partial x}∂x∂L=∂z∂L⋅∂x∂z求得zzz对xxx的导数;通过∂L∂ω=∂L∂z⋅∂z∂ω\frac{\partial L}{\partial\omega}=\frac{\partial L}{\partial z}\cdot\frac{\partial z}{\partial\omega}∂ω∂L=∂z∂L⋅∂ω∂z求得zzz对ω\omegaω的导数。
这就是一个最简单的求导计算图。黑色箭头就是最简单的从前面到后面的一个前馈,而红色箭头便是反向传播。
举一个例子吧。
上面就是一个正向的前馈处理。
这个是反向传播,首先在进行前面的传播过程我们可以得到lll对zzz的导数是555然后反向及逆行。这个样子就可以求出相应的www了。注意哈l对zzz的偏导是给出的。
现在给出一个具体的模型来处理尝试
先进行前向处理,后进行反向传播之后便可以得到解答。
2 代码使用
2.1 分块编写
咱们使用的是pytorch
进行编写哈。其中的所有的数据均是在一个叫做Tensor
的东西。然后我们又开了两个玩意儿一个是他的数据datadatadata也即是权重,另一个是gradgradgrad也就是所谓的梯度也即是损失函数对权重的导数。这两个就是最重要的两个参数。
因此对于一个普通的最简单的线性模型y^=x×ω\hat{y}=x\times \omegay^=x×ω而言他的计算图绘制如下所示。
在进行代码编写的时候最重要的就是整出这样子的计算图。
现在让我们正式开始代码的编写。
首先引入库,同时定义训练数据,并初始化权重,同时开启自动保存梯度的开关。
import torch # 引入库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 # 开启自动保存梯度的开关
注意:w.requires_grad = True
这个表示的是需要进行梯度的计算,并开启自动保存。
然后咱么开始各个函数的配置。
首先就是前馈相乘函数
forward()
其次就是计算损失的损失函数
loss()
def forward(x): #前馈相乘函数return x * wdef loss(x, y): # 计算损失的损失函数y_pred = forward(x)return (y_pred - y) ** 2
注意:xxx和www是一个数乘,但是xxx并不是一个tensortensortensor需要将xxx转换成tensortensortensor即可。
现在就可以进行深度学习求解权重的处理了。
这里给一个以循环轮数为循环变量,同时循环的最大轮数为100轮;
其次读取咱们的训练数据;
开始进行计算图的前馈模拟;
前馈完毕后进行反向传播
backward()
然后重新更新这个权重,
更新好之后将权重里面的梯度数据进行一个清理操作即可。
print("predict (before training)", 4, forward(4).item())for epoch in range(100): #以循环轮数为循环变量,同时循环的最大轮数为100轮for x, y in zip(x_data, y_data): #读取咱们的训练数据l = loss(x, y) #计算图的前馈模拟l.backward() #进行反向传播print('\tgrad:', x, y, w.grad.item())w.data = w.data - 0.01 * w.grad.data #重新更新这个权重w.grad.data.zero_() #将权重里面的梯度数据进行一个清理print("progress:", epoch, l.item())
print("predict (after training)", 4, forward(4).item())
注意:backward())
这个函数用来反向传播并且计算路上的每一个梯度,同时将这个求出来的梯度存储在w
里面。
但是一旦进行backward
之后loss
便被释放了,这个计算图就没了。
注意:获取梯度的数据方法,注意这个grad
也是一个tensor
,一定要用data
进行计算,要不然的话会整出一个新的计算图了。
item是将梯度的数值直接拿出来变成一个标量。防止产生计算图。
权重的更新一定要使用data而非直接用张量计算。防止出现计算图。
注意:将权重里面的梯度数据全部进行一个清零操作。否则在下一轮的时候上一轮的梯度数据会进行一个重合操纵,这样就出问题了。
这就是使用pytorch
来进行反向传播的操作。
2.2 代码综合
import torch # 引入库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 # 开启自动保存梯度的开关def forward(x): #前馈相乘函数return x * wdef loss(x, y): # 计算损失的损失函数y_pred = forward(x)return (y_pred - y) ** 2print("predict (before training)", 4, forward(4).item())for epoch in range(100): #以循环轮数为循环变量,同时循环的最大轮数为100轮for x, y in zip(x_data, y_data): #读取咱们的训练数据l = loss(x, y) #计算图的前馈模拟l.backward() #进行反向传播print('\tgrad:', x, y, w.grad.item())w.data = w.data - 0.01 * w.grad.data #重新更新这个权重w.grad.data.zero_() #将权重里面的梯度数据进行一个清理print("progress:", epoch, l.item())
print("predict (after training)", 4, forward(4).item())
3 课后作业
3.1 换一组变量进行推导并绘制其计算图
现在给出这个题目的解答。
1. 正向传播(前馈)
2. 反向传播
3. 计算图的绘制
3.2 增加模型复杂度继续进行pytorch
求解
现在给出这个题目的解答
- 正向传播
- 反向传播
- 计算图绘制
3.3 尝试使用二次函数模型进行反向传播的求解
现在给出这个题目的解答