• BP算法完整推导 2.0 (下)


    上篇主要阐述 BP算法的过程, 以及 推导的 4 大公式的结论, 现在呢要来逐步推导出这写公式的原理. 当理解到这一步, 就算真正理解 BP算法了. 也是先做一个简单的回顾一下, 不是很细, 重点在推导, 不清楚就结合图像呀, 其实很直观的. 全篇其实就是在求偏导, 引入中间变量, 应用链式法则 而已.

    BP算法-变量声明

    重点是理解 反向 即从 从右到左 的方向哦;

    • (w^l_{jk}) 第 l 层, 第 j 个节点, 到 第 ((l-1)) 层的 第 k 个节点的 权值 (weight) ( 反向, 反向, 方向, 反向, 说了4遍)
    • (w^l) 第 l 层, 的 权值矩阵, 第 j 行, 第 k 列 为 (w^l_{jk})
    • (b_j^l) 第 l 层, 第 j 个节点 的 bias (偏置)
    • (b^l) 第 l 层, 的 bias 向量
    • (a^l_j) 第 l 层, 第 j 个节点的 激励 (activation)
    • (a^l) 第 l 层, 的 激励 向量

    假设激活函数(sigma) , 根据网络 层次间的 映射(加权求和) 的关系, (每个神经元的模型):

    单个神经元: (a^l_j = sigma(sumlimits _k w^l_{jk} a^{l-1}_k + b^l_j))

    该层的神经元: (a^l = sigma (w^l a^{l-1} + b^l))

    如不能理解每个变量代表的意义, 就看图, 非常直观的呀

    (z^l = w^l a^{l-1} + b^l)

    (z^l) The weighted input to the neurons in layer l.

    再定义一个中间变量 (z^l) 即 第 l 层神经元的 加权求输入向量, 其分量, (z^l_j) 为第 l 层, 第 j 个神经元的 加权求和输入.

    (z^l_j =sum limits_k w_{jk} a_k ^{l-1} + b^l_j)

    于是呢, 对于每个节点的输出, 就可以简单表示为 (向量形式哈) :

    (a^l = sigma(z^l))

    然后来看定义 损失函数, 采用咱最熟悉的 平方损失 的形式:

    • (y = y(x)) 样本 x 的标签向量 (期望输出)

    • (a^L = a^L(x)) 样本 x 的网络输出激励向量

    • n : 表示样本数量; L 表示网络层数

    样本 x 表示向量, 每个分量也是一个向量(多特征), 对应于数据的每一行. 因此, x 写出来就是 nxp的矩阵

    (C = frac {1}{2n} sum limits_x ||y(x) - a^L(x)||^2)

    这个 0.5 都懂哈, , 没啥特定意义. 就是求导的时候, 式子的2范数, 要把2拿下来 再 乘0.5, 就为1 , 形式上美观而已.

    公式1: C 对于 - 输出层的梯度

    梯度, 在BP中, 就是误差

    (delta_j^L = frac {partial C}{partial a^L_j} sigma'(z^L_j) = abla _a C odot sigma'(z^L_j))

    过程: (理解图 和 求导链式法则哦)

    因为, (delta_j^L = frac {partial C}{partial a^L_j} frac {partial a^L_j}{partial z^L_j}) , 而 (a^L_j = sigma (z^L_j)), 因此, (delta_j^L = frac {partial C}{partial a^L_j} sigma'(z^L_j))

    • (frac {partial C}{partial a^L_j}​) 表示代价函数 C 对于 输出层 第 j 个节点 的激励变化程度.
    • (sigma'(z^L_j)) 表示 这第 j 个节点, 对于上层 加权输入 的变化程度.

    如果使用上面给定的 哈达玛积公式, 写成矩阵形式的话, 就变成了:

    ( abla _a C odot sigma'(z^L_j))

    • 向量 ( abla_a C) 的第 j 个元素误差为: (frac {partial C}{partial a^L_j} = abla_a C = (a^L - y))
    • $ delta ^L = (a^L - y) odot sigma'(z^L)$

    过程:

    ( abla_a C) 即是对 (C_x = frac {1}{2} ||y - a^L||^2) 的求导而得 ( (y-a^L) )

    公式2: C 对于- 中间层 的梯度

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

    即假设已经知道 (l+1) 层的误差, 通过 (l+1) 层 和 l 层之间的 权值矩阵 w, 将误差进行回传, 得到 l 层的误差

    推导:

    根据上面 z 和 每层的节点的 误差定义 (偏导数作为误差) ,则第 l+1 层, 的第 k 个节点 的误差表示为:

    (delta ^{l+1}_k = frac {partial C} {partial z_k^{l+1}})

    对于 l 层, 第第 j 个节点, 的误差表示为:

    (delta ^l = frac {partial C}{partial z^l_j}) 这个是定义来的, 然后根据 层 之间的 加权输入链式法则 展开为有关于 (l +1) 层的变量

    (=sum limits_k frac {partial C}{partial z^{l+1}_k} frac {partial z^{l+1}_k}{partial z^l_j} = sum limits_k delta^{l+1}_k frac {partial z^{l+1}_k}{partial z^l_j})

    第一项已经知道了, 继续探讨下 第二项 (注意变量的下标, 结合图形来理解)

    (z^{l+1}_k = sumlimits_j w_{kj}^{l+1} a^l_j + b^{l+1}_k = sumlimits_j w_{kj}^{l+1} sigma (z^l_j) + b^{l+1}_k)

    (frac {partial z^{l+1}_k}{partial z^l_j} = w^{l+1}_{kj} sigma '(z^l_j)) 这里 (sum) 是没有了, 因为跟 其他的 j 项 是没有关系的.

    因此,

    (delta ^l = sum_k w^{l+1}_{kj} sigma '(z^l_j) delta_k ^{l+1})

    (delta ^l = sigma '(z^l_j) sum_k w^{l+1}_{kj}delta_k ^{l+1}) 拎出 (sigma) 是因为 (sum) 对其不起作用, 可看作一个常数放在外面.

    这个写成 哈达玛积的形式, 也就是上面的 (delta ^l = ((w^{l+1})^T delta ^{l+1}) odot sigma '(z^l))

    我个人感觉, 就直接写成推导的式子挺好的, 写为了 哈达玛积 反而有些让人看不懂, 而且吧, 我还容易写错. 都是矩阵嘛, 真的很容易就写错了, 这样反而造成更大的误解...不过呢, 多学学 debug 也是蛮重要的.

    ps: 我现在就特别喜欢 debug 或者找 帮小伙伴找 bug 还有代码重构, 我感觉这是一个最为高效的互相学习交流的方式, 既学习别人的开发思路和代码风格 , 同时也跟别人分享自己的思路, 蛮有趣的体验哦.

    公式3: C 对于 - Bias 的梯度

    (frac {partial C}{partial b^l_j} = delta^l_j) 写成矩阵就是: (frac {partial C}{partial b} = delta)

    推导:

    (z^l_j = sumlimits_k w^l_{jk} a^{l-1}_k + b_j ^l) 这个上面的 l 层 和 l+1 层是一样的, 重在理解层间的, 加权求和输入再激活的 关系

    易知: (frac {partial z^l_j}{partial b^l_j} = 1)

    则: (frac {partial C}{partial b^l_j} = frac {partial C}{partial z^l_j} frac {partial z^l_j}{partial b^l_j} = frac {partial C}{partial z^l_j} = delta^l_j)

    公式4: C 对于 - 权值 的梯度

    (frac {partial C}{partial w^l_{jk}} = a^{l-1}_k delta^l_j) 也可这样表达为: (frac {partial C}{partial w} = a_{in} delta_{out})

    推导: (跟公式3一样, 还是厉害 层间的关系, 注意理解每个下标的含义哦)

    (z^l_j = sumlimits_k w^l_{jk} a^{l-1}_k + b^l_j)

    易知: (frac {partial z^l_j}{partial w^l_{jk}} = a^{l-1}_k)

    则: (frac {partial C}{partial w^l_{jk}} = frac {partial C}{partial z^l_j} frac {partial z^l_j}{partial w^l_{jk}} = a^{l-1}_k frac {partial C}{partial z^l_{j}} = a^{l-1}_k delta^l_j)

    • (a_{in}) 表示 输入权值 w 上层 神经元的 激励实值

    • (delta_{out}) 表示 本层 权值 w 输入到 下层 神经元的 误差实值

    如果 (a_{in}) 接近于0, 则表示该 权值的梯度也接近0, 此时称该神经元的权值学习比较慢, 即梯度变化时, 对代价函数的影响较小. (激励值过低的神经元学习很慢), 这种神经元呢, 也被称为 饱和神经元 这就是我们期望的效果呀.

    饱和神经元: (先看咱上边推导出的结论)

    (sigma(x) = frac {1}{1+e^{-x}})

    (sigma'(x) = sigma(x) (1-sigma(x)))

    (delta_j^L = frac {partial C}{partial a^L_j} sigma'(z^L_j) = abla _a C odot sigma'(z^L_j))

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

    (frac {partial C}{partial w^l_{jk}} = a^{l-1}_k delta^l_j)

    从激励函数来看,

    (sigma(x) = frac {1}{1+e^{-x}}) 是一个 " s " 型的函数, 当 x = 0的时候, 激励值为 0.5

    当某层神经元的 加权输入, **过大 或 过小 ** 时, 则 输出的激励值 要么接近1, 要么接近于 0, 这样呢, 对于 C 来时, 会导致 激励的导数值为 0, 从而 误差减小, 即权值学习很慢, 逐渐接近 饱和 (saturated) , 逐渐停止学习.

    理解上面式子, 因为 (sigma'(x)) 变化所带来的一连串影响哦

    一个权值学习慢, 可能是因为 输入的神经元的激励很小, 或者其输出的神经元接近饱和( 激励过大接近1, 或过小, 接近0) .

    小结 - 四大公式 及 矩阵表达

    • 公式1: C 对于 - 输出层的梯度: (delta_j^L = frac {partial C}{partial a^L_j} sigma'(z^L_j) = abla _a C odot sigma'(z^L_j))

    • 公式2: C 对于 - 中间层的梯度: (delta ^l = ((w^{l+1})^T delta ^{l+1}) odot sigma '(z^l))

    • 公式3: C 对于 - Bias 的梯度: (frac {partial C}{partial b^l_j} = delta^l_j)

    • 公式4: C 对于 - 权值 的梯度: (frac {partial C}{partial w^l_{jk}} = a^{l-1}_k delta^l_j)

    然后来写一波矩阵表达. (假设 隐含层是 m个节点, 输出层是 n 个节点)

    为啥要矩阵表达呢, 首先是比较简洁呀, 公式上, 虽然有点不好理解. 但, 写成矩阵, 容易编程实现呀

    公式1 可写为: (delta^L = Sigma(z^L) abla_aC)

    (Sigma) 这不是求和, 我写的 latex 是这样的: Sigma 读作 " C 格码", 是个对角阵 (Sigma(z^L))主对角线元素为: (sigma (z^L_j))

    维数: n x n, nx1 ==> nx1 的向量; L 表示输出层

    公式2 可写为: (delta^l = Sigma (z^L) (w^{l+1})^T delta ^{l+1}))

    跟前面转法一样的, 维数分别是: mxm, (nxm) ^T, nx1 ==> m x 1 的向量, (l) 表示 中间的任意一层

    公式3 可写为: (frac {partial C}{partial b^l} = delta^l)

    公式4 可写为: (frac {partial C}{partial w} = a^{l-1} (delta^l)^T)

    w 是 mxn 的矩阵; (delta ^l) 是 l 层的误差向量, nx1维; $a^{l-1} 是 (l-1)层 $ 的激励向量, mx1维.

    BP算法步骤 (SGD)

    总体来看, BP 算法, 就2步

    • 前向计算出误差
    • 误差后传, 更新权值

    搞定, 还差 撸一把numpy 代码, 这个不是很难哦, 网上也有很多, 不在这些了, 私下自己后面再编写吧, 毕竟思路的很清楚了.

  • 相关阅读:
    ListView添加HeaderView的顺序问题
    shape与selector配合使用实现Tab下划线效果
    在java代码中设置TextView的字体大小,单位设为sp
    一个textView中的文字设置成两种颜色
    Android studio生成类图
    linux常用命令总结
    主机CPU与显卡选择
    电脑与显示器四种接口
    Linux shell
    Linux 常用命令二
  • 原文地址:https://www.cnblogs.com/chenjieyouge/p/12234705.html
Copyright © 2020-2023  润新知