pytorch学习4-反向传播


在前篇的线性模型中

\[\widehat y = \omega x\]

如果以神经网络的视角代入来看,则x为输入层,即input层,ω为权重,y^为输出层。在神经网络中,通常将ω以及*计算操作的部分合并看做一个神经元(层)。而神经网络的训练过程即为更新ω的过程,其更新的情况依赖于 \(\frac{\partial loss}{\partial \omega}\) ,而并非 \(\frac{\partial \widehat y}{\partial \omega}\)

image-20210301201835811

然而,对于复杂模型而言求解过程就复杂很多。在图示的神经网络中,每个结点为一个神经元,结点之间的连线为权重。

image-20210301202712216

由图上可知,输入层与隐含层第一层之间就有5∗6=30个权重,隐含层的第一层与第二层之间又有6∗7=42个权重,以此类推,上图中共有30+42+49+42+30=193个权重需要计算,传统的列表达式的方式是无法完成的。

计算图中的神经网络

在计算图中,绿色的模块为计算模块,可以在计算过程中求导。MM为矩阵乘法,ADD为加法。

image-20210301210756389

而上图左式中,可以化简得到如下公式

\[\widehat y = W_2(W_1X+b_1)+b_2=W_2W_1X+(W_2b_1+b_2)=WX+b\]

也就是说,在这个结构下单纯的增加层数,并不能增加神经网络的复杂程度,因为最后都可以化简为一个单一的神经网络。

改进方法

在每层网络结构中,增加一个非线性的变换函数(激活函数),比如sigmod函数

image-20210301211647298

反向传播过程

前馈计算

在某一神经元处,输入的x与ω经过函数f(x, ω)的计算,可以获得输出值z,并继续向前以得到损失值loss.

在向前计算的过程中,在f(x, ω)的计算模块中会计算导数 \(\frac{\partial z}{\partial x}\) 以及 \(\frac{\partial z}{\partial \omega}\) ,并将其保存下来(在pytorch中,这样的值保存在变量x以及ω中)。

image-20210301213441765

反向传播

由求导的链式法则,求得loss以后,前面的神经元会将 \(\frac{\partial loss}{\partial z}\) 的值反向传播给原先的神经元,在计算单元f(x, ω)中,将得到的 \(\frac{\partial loss}{\partial x}\) 与之前存储的导数相乘,即可得到损失值对于权重以及输入层的导数,即 \(\frac{\partial loss}{\partial x}\) ,以及 \(\frac{\partial loss}{\partial \omega}\) ,基于该梯度才进行权重的调整。

image-20210301213933839

Pytorch中的前馈与反馈

利用pytorch进行深度学习,最主要的是==构建计算图==

Tensor 张量

Tensor中重要的两个成员,data用于保存权重本身的值ω,grad用于保存损失函数对权重的导数 \(\frac{\partial loss}{\partial \omega}\) ,grad本身也是个张量。对张量进行的计算操作,都是建立计算图的过程。

image-20210301215917238

import torch

x_data = [1.0, 2.0, 3.0]
y_data = [2.0, 4.0, 6.0]

#赋予tensor中的data
w = torch.Tensor([1.0])
#设定需要计算梯度grad
w.requires_grad = True

#模型y=x*w 建立计算图
def forward(x):
    '''
    w为Tensor类型
    x强制转换为Tensor类型
    通过这样的方式建立计算图
    '''
    return x * w

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()
        #grad.item()取grad中的值变成标量
        print('\tgrad:',x, y, w.grad.item())
        #单纯的数值计算要利用data,而不能用张量,否则会在内部创建新的计算图
        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())

本文参考自B站《PyTorch深度学习实践》P4