详解反向传播算法(下)

原文出处:详解反向传播算法(下)


详解反向传播算法(下)

详解反向传播算法(下)

晓雷 晓雷
10 个月前

神经网络结构图:

示例网络图

其中C是损失函数,例如C可以取:

梯度下降(SGD)进行学习时,核心问题是求解损失函数C关于所有网络参数w_{jk},b_j的偏导数\frac{\partial C}{\partial w_{jk}} ,\frac{\partial C}{\partial b_j}  。 根据详解反向传播算法(上) 我们已经知道用反向传播算法可以“一次反向计算”得到损失函数C关于网络中所有参数的偏导数。模仿详解反向传播算法(上) 的推理过程,我们首先画出上面网络图的详细计算图:再看看具体怎么样反向传播求偏导数。

神经网络计算图

对应计算图如下:(只展开了最后两层的计算图):

绿色代表权重参数w_{jk},橙色代表基底参数b_j。可见虽然网络图上只是简单几条线,计算图还是蛮复杂的。

现在我们在计算图箭头上标出对应的偏导数(只标出了一部分)。

反向传播四公式


上面计算图上每一个节点关于前一个节点的偏导数都可以求得,根据求导的链式法则,想要求损失函数C关于某一节点的偏导数,只需要“把该节点每条反向路径上的偏导数做乘积,再求和”即可。(w_{jk},b_j分别对应绿色和橙色的节点)

现在我们已经可以在计算图上求得损失函数C关于模型参数的偏导数\frac{\partial C}{\partial w_{jk}} ,\frac{\partial C}{\partial b_j}  。但是还不够优雅,反向传播算法要优雅的很多,它通过定义一个损失(\delta_j^l),先逐层向后传播得到每一层节点的损失(\delta_j^l),再通过每一个节点的损失(\delta_j^l)来求解该节点的\frac{\partial C}{\partial w_{jk}} ,\frac{\partial C}{\partial b_j}

首先记损失函数C关于l层的第j个元素的偏导为:\delta_j^l \equiv \frac{\partial C}{\partial z_j^l}

最后一层

对于最后一层(L层)的元素j会有:

\delta_j^L = \frac{\partial C}{\partial z_j^L}=\frac{\partial C}{\partial a_j^L} \cdot \frac{\partial a_j^L}{\partial z_j^L} = \frac{\partial C}{\partial a_j^L} \cdot \sigma^{'}(z_j^L)

向量化为:

\bm \delta^L = \begin{pmatrix} \delta_1^L \\\vdots \\ \delta_j^L \\   \vdots\\  \delta_n^L \end{pmatrix}= \begin{pmatrix} \frac{\partial C}{\partial a_1^L} \cdot \sigma^{'}(z_1^L) \\\vdots \\ \frac{\partial C}{\partial a_j^L} \cdot \sigma^{'}(z_j^L) \\   \vdots\\  \frac{\partial C}{\partial a_n^L} \cdot \sigma^{'}(z_n^L) \end{pmatrix}= \begin{pmatrix} \frac{\partial C}{\partial a_1^L} \\\vdots \\ \frac{\partial C}{\partial a_j^L} \\   \vdots\\  \frac{\partial C}{\partial a_n^L} \end{pmatrix}\odot  \begin{pmatrix} \sigma^{'}(z_1^L) \\\vdots \\ \sigma^{'}(z_j^L) \\   \vdots\\  \sigma^{'}(z_n^L) \end{pmatrix} =  \bm \nabla_aC \odot  \sigma^{'}(\bm z^L) (BP1)

其中\odot的操作是把两个向量对应元素相乘组成新的元素。

后一层传播到前一层

由前面计算图中L和L-1层所标注的偏导数,可得到倒数第一层(L-1)元素j的损失为:(请仔细对照前面的计算图)\delta_j^{L-1} = (\sum_{j=1}^n{\frac{\partial z_j^L}{\partial a_{k}^{L-1}}  \delta_j^L }) \cdot \sigma_{'}(z_j^{L-1}) = (\sum_{j=1}^n{w_{jk}^L \delta_j^L } ) \cdot  \sigma_{'}(z_j^{L-1})  =\begin{pmatrix} w_{1k}^L \cdots w_{jk}^L \cdots  w_{nk}^L\\  \end{pmatrix} \begin{pmatrix} \delta_1^L \\  \vdots \\  \delta_j^L\\\vdots\\\delta_n^L \end{pmatrix}\cdot \sigma^{'}(z_j^{L-1})

向量化:\delta^{L-1} = ((w^{L})^T\delta^{L} \odot \sigma^{'}(z^{L-1}) )

这启发我们后一层(l+1层)的损失\delta^{l+1} 如何传播到前一层(l层)得到\delta^l。(只需要把L用l+1替换,L-1l替换)就得到了逐层传播损失的公式:

\bm \delta^{l} = ((\bm w^{l+1})^T \bm \delta^{l+1} \odot \sigma^{'}(\bm z^{l}) )(BP2)

关于b_j^l的偏导数

\frac{\partial C}{\partial b_j^l} =\frac{ \partial C}{ \partial z_j^l} \frac{\partial z_j^l}{\partial b_j^l} = \delta_j^l \cdot 1 = \delta_j^l(BP3)

向量化:\frac{\partial C}{\partial b^l} =\bm \delta^l


关于w_{jk}^l的偏导数

\frac{\partial C}{\partial w_{jk}^l} =\frac{ \partial C}{ \partial z_j^l} \frac{\partial z_j^l}{\partial w_{jk}^l} = \delta_j^l \cdot a_k^{l-1} (BP4)

向量化:\frac{\partial C}{\partial w_{j\cdot }^l} =\begin{pmatrix}\delta_j^l  a_1^{l-1}  \\  \vdots \\  \delta_j^l  a_k^{l-1} \\\vdots\\\delta_j^l  a_n^{l-1}  \end{pmatrix}=\delta_j^l \cdot\begin{pmatrix}  a_1^{l-1}  \\  \vdots \\    a_k^{l-1} \\\vdots\\  a_n^{l-1}  \end{pmatrix}= \delta_j^l \cdot \bm a^{l-1} \Rightarrow

\frac{\partial C}{\partial w^l} = \begin{pmatrix}\delta^l_1 \cdot \bm a^{l-1} \\\vdots \\\delta^l_j \cdot \bm a^{l-1}\\\vdots\\\delta^l_n \cdot \bm a^{l-1}\\\end{pmatrix}= \begin{pmatrix}\delta^l_1   \\\vdots \\\delta^l_j \\\vdots\\\delta^l_n \\\end{pmatrix}\cdot \begin{pmatrix} a^{l-1}_1  \cdots a^{l-1}_k \cdots a^{l-1}_n  \end{pmatrix}= \bm \delta^l \cdot (\bm a^{l-1})^T

至此就得到了反向传播的4个公式:

图片来自:Neural networks and deep learning
反向传播算法流程:

流程图来自: Neural networks and deep learning



本文主要参考 Neural networks and deep learning,原作者写的也不错,不过个人觉得如果按照计算图会更加直观,基本不需要数学推导过程,用肉眼看图就可以理解反向传播的四个公式。当然前提是计算图要画的清晰明白。花了半天时间来写这篇文章,其中画图花费了80%的时间,尤其是计算图改了N次,仍然可能存在错误,欢迎指正~

点赞(分享)就是对文章作者的最大鼓励~

------下面只是备份下用过的公式,以备后面修改使用 ------------

a_j^L=\sigma(z_j^L)  a_2^L=\sigma(z_2^L)  z_j^L=\sum_{k=1}^{K}{(w_{jk}^L \cdot a_{k}^{L-1}) } +b_{j}^L z_1^L=\sum_{k=1}^{4}{(w_{1k} \cdot a_k^{L-1}) } +b_{1} z_2^L=\sum_{k=1}^{4}{(w_{2k} \cdot a_k^{L-1}) } +b_{2} \frac{\partial a_j^L}{\partial z_j^L}  = \sigma^{'}(z_j^L) \frac{\partial z_j^L}{\partial b_j^L} = 1 \frac{\partial z_j^L}{\partial w_{jk}^L}  =a_k^{L-1} \frac{\partial z_2^L}{\partial a_{k}^{L-1}}  =w_{2k}^L
\frac{\partial a_k^{L-1}}{\partial z_k^{L-1}}  = \sigma^{'}(z_k^{L-1}) \frac{\partial z_k^{L-1}}{\partial b_k^{L-1}} = 1 \frac{\partial z_k^{L-1}}{\partial w_{km}^{L-1}}  =a_m^{L-2}

------------------------ 备份 end ----------------------------------------------


### 反向传播算法原理及实现过程 #### 1. 定义与背景 反向传播算法是一种用于训练多层前馈神经网络的方法,它通过最小化预测输出与实际目标之间的差异来调整权重。该算法由 Rumelhart 和 Hinton 提出,是当前主流机器学习和深度学习框架中不可或缺的一部分[^3]。 #### 2. 前向传播阶段 在网络接收输入信号时,数据会依次经过每一层节点直到最终得到输出结果。这一过程中涉及到了激活函数的应用: \[ z^{(l)} = W^{(l)}a^{(l-1)} + b^{(l)} \] 其中 \(z^{(l)}\) 表示第 l 层的线性组合;\(W^{(l)}\) 是连接矩阵;\(b^{(l)}\) 则代表偏置项;而 \(a^{(l-1)}\) 即为上一层的激活值。接着应用非线性的激活函数 g() 来获取新的激活状态: \[ a^{(l)}=g(z^{(l)}) \][^1]. #### 3. 计算损失函数 当获得模型对于给定样本集产生的估计之后,则可以利用特定形式的成本/损失函数 J(W,b;X,y),衡量这些预估值同真实标签 y 的差距大小。常见的有均方差(MSE) 或交叉熵(Cross Entropy Loss): \[ L(a^{(L)},y)=\frac{1}{m}\sum_{i=1}^{m}(h_\theta(x^{(i)})-y^{(i)})^2 \quad (MSE)\] 这里 m 指的是批量处理的数据量,\( h_\theta(x^{(i)}) \) 就是指代最后一层输出单元所给出的概率分布情况。\(^{(L)}\)表示最顶层即输出层[^2]. #### 4. 后向传递计算梯度 一旦确定了总成本 C ,就可以开始执行 BP 步骤了——从右至左逐级求解各参数对应的敏感度δ : \[ \delta^{(L)}=\nabla_aC⊙σ'(z^{(L)}) \] 这里的 σ' () 函数指的是对应于隐藏结点使用的激活函数之导数版本;符号 ⊙ 表明按元素相乘操作而非矩阵乘法。随后依据链式法则继续向前推进直至首端位置: \[ \delta^{(l)}=((W^{(l+1)})^\top\delta^{(l+1)})⊙σ'(z^{(l)}) \] 最后基于上述所得 δ^(l)_j 对应关系更新权值 w_ij 和阈限 θ_j : \[ Δw_{ij}=ηδ_j^{(l)}a_i^{(l−1)} \\ Δθ_j=-ηδ_j^{(l)} \] 此处 η 称作学习速率因子用来控制步长幅度. ```python def backpropagation(X, Y, weights, biases, learning_rate): # 初始化变量... for i in range(iterations): # 执行一次完整的正向传播 # ...省略具体细节... # 开始BP流程:先针对末端部分做初步设定 delta_output_layer = np.multiply( -(Y - output), sigmoid_derivative(output)) deltas = [None]*len(weights) deltas[-1] = delta_output_layer # 接着往回走遍历其他层次 for layer_index in reversed(range(len(deltas)-1)): weight_matrix_next_level = weights[layer_index+1] current_delta = deltas[layer_index+1] weighted_sum_of_deltas = np.dot(weight_matrix_next_level.T, current_delta) activation_function_prime = \ relu_derivative if use_relu else sigmoid_derivative deltas[layer_index] = np.multiply( weighted_sum_of_deltas, activation_function_prime(layer_activations[layer_index])) # 更新所有可调参量 for idx, delta in enumerate(reversed(deltas)): gradient_wrt_weights = np.outer(delta, activations[idx]) gradients.append((gradient_wrt_weights * learning_rate).T) updated_weight_matrices.insert(0,weights[idx]-gradients.pop()) new_biases = list(map(lambda d:b-d*learning_rate,deltas,biases)) return updated_weight_matrices,new_biases ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值