BP算法完整推导 2.0 (上)
前面的笔记已经把 BP算法给推导了, 那4大公式, 核心就是 求偏导数的链式法则, 这篇, 再来跟着大佬来推一波, 目的是为了加深印象. 关于记忆这个话题, 心理学家,其实早已经给出了答案, 最好的记忆方式, 就是重复, 写了这么多的笔记, 其实大多内容都是重复的, 交叉的, 反复了, 但不同是, 每次反复, 其实都是一个认知理解上升的过程, 觉得还是很有必要的.
BP算法-变量声明
回顾历史
还是跟之前一样的, 先做一个简单的介绍, 然后对变量进行声明, 力求跟写代码的逻辑一样, 严谨, 规范, 有注释, 逻辑清晰, 模块化和面向对象
先是来简单介绍一波BP算法.
最早在1970年代就已经有人提出了, 直到1986年, 深度学习的大佬们才在论文中阐述其重要性.
在80年代中期, 这些大佬分别独立发现了 误差反向传播算法 (Error Back Propagation Training) 简称BP, 解决了, 多层神经网络, 隐含层连接权值学习问题.
BP 用来干嘛: 根据训练的误差, 来 动态 更新节点之间的 权值 .
分别独立发现: 好比牛顿和莱布尼兹 分别从各自领域创建了微积分.
不由感慨, 成功的大佬, 都是类似的, 我等凡人失败的借口, 总是各有各的说辞, 真的好扎心, 而又无可奈何呀.
BP算法相对于其他的神经网络学习算法会快很多, 这样就扩展了神经网络的解决问题的能力和领域. 直到目前, BP算法依然是深度学习的主要基石.
BP算法的核心是 求解误差函数 Loss 相对于权重 weights 和偏置 bias 的偏导, 来揭示 一旦改变 w, b 如何影响网络的整体行为.
而求解过程呢, 从数学上来说, 就是 多元变量求偏导的链式法则. 大一的微积分基础呀, 没错, 其实一点都不复杂的.
变量声明
重点是理解 反向 即从 从右到左 的方向哦;
- \(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(\sum\limits _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 , 形式上美观而已.
代价函数2大假设
假设一:
上面的, 具体到每个值(样本) , 代价函数可以表示为, 每个训练样本 x (是个向量哈) 的代价函数 \(C_x\) 的平均值
\(C_x = \frac {1}{2} ||y - a^L||^2\)
\(C = \frac {1}{n} \sum\limits_x C_x\)
假设二:
代价函数可以表示为, 神经网络 输出层的 函数
\(C = \frac{1}{2} ||y-a^L||^2 = \frac {1}{2} \sum\limits_j (y_j - a_j^L)^2\)
这里的 \(y_j 是标签值, 表示一个常量, a^L_j 表示输出层向量 a^L 的第 j 个分量, 也是一个常量; C 是输出层向量 a的函数\)
矩阵的 哈达玛积 (Hadamard Product)
定义为, 两个 同维度 的矩阵 / 向量, 对应位置的元素的乘积, 组成的一个新的矩阵/ 向量, 记为 \(A \odot B\)
case:
假设 \(\alpha, \beta\) 是两个同维度的向量, 其 哈达玛积 为, 向量各位置的分量 的逐元素相乘.
\((\alpha \odot \beta)_j = \alpha_j * \beta_j\)
\([1, 2] \ \odot \ [3, 4] = [1*3, 2*4] = [3, 8]\)
BP算法 - 四大公式
理解 梯度的反方向
首先就是看看, 网络中, 一个神经元节点的 权值 变化的话, 对整个网络的 代价会产生什么样而影响.
假设, 给第 l 层的 第 j 个神经元的节点值 \(z^l_j\) 加上一个增量, 则对应的 激励输出变化为
\(\sigma(z^l_j) \rightarrow \ \sigma(z^l_j + \Delta z^l_j)\)
经过网络传播呢, 代价函数的 变化量 为: \(\frac {\partial C} {\partial z^l_j} \Delta z^l_j\)
变化量, 这个式子就是, 微分和导数的关系, \(df(x) = f'(x) dx\) 嗯, 或者理解为, 泰勒级数一阶近似也可以.
假设该 梯度 (偏导数值) \(\frac {\partial C} {\partial z^l_j}\) 非常大 (可正, 可负)
始终牢记咱的目标是使得代价函数 C 的值越小越好, 也就是要找到 某一个点, 使得在该点导数值 == 0
- \(\frac {\partial C} {\partial z^l_j}\) 非常大, 则表示 \(z^l_j\) 对于整个网络的损失函数影响很大, 然后我们会选择与梯度符号相反的增量. (目的是让 "df" 接近 0, 接近函数 极值 的位置)
- \(\frac {\partial C} {\partial z^l_j}\) 非常小, 则表示扰动的增量, 不论取何值, 对代价函数的影响很小, 则该神经元对于代价函数倾向于最优 , 也是是到达了函数的 局部极值点.
梯度方向
梯度: 多元函数的偏导数做组成的向量, 本质是一个向量函数.
方向导数: 与偏导数更广的概念, 偏导是沿着固定的轴方向, 是方向导数的特定情景
方向: 是针对向量来说的, 向量具有大小和方向
- 方向导数最大的方向, 为梯度方向, 其导数值就是梯度的模.
- 方向导数最小的放俩, 为梯度反方向, 其导数方向是梯度的摸
在 ML 中我们绝大多数场景是要 寻找梯度的最小, 最好是零向量, 这样就是函数的 "极值" 所在的位置.
因此我们总是尝试沿着 梯度的负方向去求值最小的导数值, 因而求解函数的 极值.
这些就是很基础的, 高等数学的概念呀, 真的越接近本质, 越发现世界就是有基本的元素0, 1构成的, 也许. "道生一, 一生二, 二生三, 三生万物" 这是多么朴素的真理呀, 现在想想.
即想说明的是, 因为是要顾及整个网络结构嘛, 绝对不允许, 特别牛逼的节点存在 对损失函数影响很大的那种, 嗯, 这种思想和 集成学习是一样的, 都是平凡普通, 才能构成伟大, 不允许突出的兄弟.
梯度大则说训练得不够好, 因为在这个节点方向, 只要一稍微扰动该神经元, 就会对代价造成很大的影响;
反之, 梯度小则说明训练的比较好, 都是同一水平的兄弟, 然后不论这个普通的兄弟发生了什么事情, 都不会对整个代价造成特别大的影响.
这点就特像一些大公司里的螺丝钉, 多你一个不多, 少你一个也不太影响, 整个结构来时还是相对均衡和稳固的呀.
根据梯度的这种特性, 于是, 我们可以定义, 第 l 层, 第 j 个神经元 的误差为:
\(\delta ^l_j = \frac {\partial C}{\partial z^l_j}\)
tips: 算是第一遇见, 将导数值, 作为误差, 来训练, 这个脑洞, 真滴是可以的哦.
于是呢, BP算法就通过, 先求出各层神经元的偏导数,作为误差, 即相当于对代价C 求关于 权重和偏置 梯度
\(\frac {\partial C} {\partial w^l_{jk}}\) , 以及 \(\frac {\partial C}{\partial b_j^l}\)
上篇就先到这吧, 一次不适合写太长, 信息太多会吸收不了的, 主要是理解BP到底在干嘛, 误差如何定义等这些概念的理解, 下篇就具体的公式推导和代码, 只有充分理解了上篇, 才能搞明白下篇的公式是是在做什么, 才能实现后面的手撸BP的呀.