从 pyTroch 开始聊起自动求导 (2)

从 pyTroch 开始聊起自动求导 (2)持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第31天,点击查看活动详情 torch.autograd 是推动训练的自动求导引擎,通过今天分享将会对 autograd 是如何推动

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第31天,点击查看活动详情

torch.autograd 是推动训练的自动求导引擎,通过今天分享将会对 autograd 是如何推动模型训练有一个概念上理解。

神经网络可以看做嵌套函数集合,那么什么是嵌套函数呢?
f ( g ( x ) ) f(g(x))
,嵌套函数通常一个嵌套在非线性激活函数的线性函数,学习的是线性函数的权重和偏置。这些权重和偏置参数都是用 tensor 来表示。

训练过程通常包含两个阶段

  • 前向传播: 在前向传播中,数据经过一层层函数得出一个对数据关心方面的预测
  • 反向传播:

链式法则

理解链式法则对于理解反向传播很重要,我们从最最简单的网络讲起吧,这个网络足够简单,每一层只有一个神经元

screenshot_001.png

图上这个神经网络的参数就是包括 3 个权重和 3 个偏置,每一层提供一个权重和一个偏置。

我们的目标是理解损失函数对于这些变量的敏感程度,然后根据这些变量对损失函数影响程度,来调节这些变量,让损失函数降低得最快

接下来先看最后两个神经元,这里上标表示神经元层数,一共有
L L
层,随意最后表示为
a ( L ) a^{(L)}
,前一层神经元输出可以表示为
a ( L 1 ) a^{(L-1)}
,这里上标表示变量所位于神经网络层数。

screenshot_002.png

给定一个训练样本 把这个最终层激活值要接近的目标叫做 y,这里假设是一个而分类问题,那么 y 取值可能是 0 或者是 1 来表示两个不同的类别。

那么在这个简单网络中,对于单个训练样本的损失函数就可以表示为
( a ( L ) y ) 2 (a^{(L)} – y)^2
,这是平法损失函数,用
C 0 C_0
表示损失函数,这里下标是表示样本。

接下里我们看一看,这里上一层输出
a ( L 1 ) a^{(L-1)}
经过一个线性变换为
w ( L ) a ( L 1 ) + b ( L ) w^{(L)}a^{(L-1)} + b^{(L)}
, 这里我们可以用
z ( L ) z^{(L)}
来表示
w ( L ) a ( L 1 ) + b ( L ) w^{(L)}a^{(L-1)} + b^{(L)}
那么在对线性变化的结果进行一次非线性操作,
a ( L ) = σ ( z ( L ) ) a^{(L)} = \sigma(z^{(L)})

screenshot_003.png

接下来我们我们来看
w ( L ) w^{(L)}
是对
C 0 C_0
进行影响的,可以通过图,就是要理解权重
w ( L ) w^{(L)}
微小变化会给
C C
带来多少变化,可以用
C w \frac{\partial C}{\partial w}
,

screenshot_005.png

通过这张图不难理解,
w ( L ) w^{(L)}
通过对于
z ( L ) z^{(L)}
影响,然后一路从
z ( L ) z^{(L)}
经过
a ( L ) a^{(L)}
影响到
C 0 C_{0}
的,这就是链式法则。


C 0 w ( L ) = z ( L ) w ( L ) a ( L ) z ( L ) C 0 a ( L ) \frac{\partial C_0}{w^{(L)}} = \frac{\partial z^{(L)}}{w^{(L)}}\frac{\partial a^{(L)}}{\partial z^{(L)}}\frac{\partial C_0}{a ^{(L)}}

接下来就可以一项一项进行求导,我们先把这些式子都列出了,以便进行对比


C 0 = ( a ( L ) y ) 2 z ( L ) = w ( L ) a ( L 1 ) + b ( L ) a ( L ) = σ ( z ( L ) C_0 = (a^{(L)} – y)^2\\ z^{(L)} = w^{(L)}a^{(L-1)} + b^{(L)}\\ a^{(L)} = \sigma(z^{(L)}


C 0 C_0
关于
a ( L ) a^{(L)}
的导数 就是
2 ( a ( L ) y ) 2(a^{(L)} – y)


C 0 a ( L ) = 2 ( a ( L ) y ) \frac{\partial C_0}{\partial a^{(L)}} = 2 (a^{(L)} – y)

这也就意味着导数的大小跟网络最终输出
a ( L ) a^{(L)}
和目标结果的差成正比,如果网络的输出差别很大,即便
w w
稍稍变一点 损失函数也会有很大的改变。


a ( L ) a^(L)

z ( L ) z^(L)
求导就是求 sigmoid 的导数,可以表示为


a ( L ) z ( L ) = σ ( z ( L ) ) \frac{\partial a^{(L)}}{\partial z^{(L)}} = \sigma^{\prime }(z^{(L)})

或就你选择的非线性激活函数


z ( L ) w ( L ) = a ( L 1 ) \frac{\partial z^{(L)}}{\partial w^{(L)}} = a^{(L-1)}

C 0 w ( L ) = a ( L 1 ) σ ( z ( L ) ) 2 ( a ( L ) y ) \frac{\partial C_0}{w^{(L)}} = a^{(L-1)}\sigma^{\prime }(z^{(L)})2 (a^{(L)} – y)

基于类似方法,我们可以计算
C 0 b ( L ) \frac{\partial C_0}{b^{(L)}}

C 0 a ( L 1 ) \frac{\partial C_0}{a^{(L-1)}}


C 0 b ( L ) = 1 × σ ( z ( L ) ) 2 ( a ( L ) y ) \frac{\partial C_0}{b^{(L)}} = 1 \times \sigma^{\prime }(z^{(L)})2 (a^{(L)} – y)

C 0 a ( L 1 ) = w ( L ) σ ( z ( L ) ) 2 ( a ( L ) y ) \frac{\partial C_0}{a^{(L-1)}} = w^{(L)}\sigma^{\prime }(z^{(L)})2 (a^{(L)} – y)


z ( L ) z^{(L)}

w ( L ) w^{(L)}
求导,导数为
a ( L 1 ) a^{(L-1)}
,从公式来
w \partial w
对损失函数影响程度取决于其上一层的输出
a ( L 1 ) a^{(L-1)}
,这样进一步验证了一同激活的神经元关联在一起(neurons fire together wire together)。

计算损失函数是是一个批次多个样本进行求平均来计算的如下


C w ( L ) = 1 n k = 0 n 1 C k w ( L ) \frac{\partial C}{w^{(L)}} = \frac{1}{n} \sum_{k=0}^{n-1} \frac{\partial C_k}{w^{(L)}}

梯度是由方向的所以
C = [ C w ( 1 ) C w ( 1 ) C w ( L ) C w ( L ) ] \nabla C = \begin{bmatrix} \frac{\partial C}{\partial w^{(1)}} \\ \frac{\partial C}{\partial w^{(1)}} \\ \vdots\\ \frac{\partial C}{\partial w^{(L)}} \\ \frac{\partial C}{\partial w^{(L)}} \\ \end{bmatrix}

而真实的神经网络会比这个例子复杂的多、一个网络会有多个层,每一个层会有多个神经元,这时候下标就派上用场了。

screenshot_006.png

用下标来表示某一个层的某一个神经元


C 0 = j = 1 n L 1 ( a j ( L ) y j ) 2 C_0 = \sum_{j=1}^{n_L – 1} \left(a_j^{(L)} – y_j\right)^2

screenshot_007.png

在 L 层中下标
j j
表示第 L 激活值,而
i i
表示 L-1 第
i i
激活值,他们之前权重用
w j i ( L ) w_{ji}^{(L)}
这里
j j

i i
的顺序有些奇怪,原因是是矩阵相乘。


C 0 = j = 1 n L 1 ( a j ( L ) y j ) 2 C_0 = \sum_{j=1}^{n_L – 1} \left(a_j^{(L)} – y_j\right)^2

z j ( L ) = w j 0 L a 0 ( L 1 ) + w j 1 L a 1 ( L 1 ) + w j 2 L a 2 ( L 1 ) z_j^{(L)} = w_{j0}^{L} a_0^{(L-1)} + w_{j1}^{L} a_1^{(L-1)} + w_{j2}^{L} a_2^{(L-1)}

现在当前
j j
激活函数值是由上一层所有元素加权求和得到
z j ( L ) z_j^{(L)}
然后对其
σ ( z j ( L ) ) \sigma(z_j^{(L)})
就得到了激活值


a j ( L ) = σ ( z j ( L ) ) a_j^{(L)} = \sigma(z_j^{(L)})

C 0 w j k ( L ) = j = 0 n L 1 z j ( L ) w j k ( L ) a j ( L ) z j ( L ) C 0 a j ( L ) \frac{\partial C_0}{w^{(L)}_{jk}} = \sum_{j=0}^{n_L – 1} \frac{\partial z^{(L)}_j}{w^{(L)}_{jk}}\frac{\partial a^{(L)}_j}{\partial z^{(L)}_j}\frac{\partial C_0}{a ^{(L)}_j}

现在的方程式和之前每层只有一个神经元的时候本质是一样的

screenshot_008.png

今天的文章从 pyTroch 开始聊起自动求导 (2)分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/16832.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注