上篇主要阐述 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(\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 , 形式上美观而已.

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

梯度, 在BP中, 就是误差

\(\delta_j^L = \frac {\partial C}{\partial a^L_j} \ \sigma'(z^L_j) = \nabla _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 个节点, 对于上层 加权输入 的变化程度.

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

\(\nabla _a C \odot \ \sigma'(z^L_j)\)

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

过程:

\(\nabla_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 = \sum\limits_j w_{kj}^{l+1} a^l_j + b^{l+1}_k = \sum\limits_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 = \sum\limits_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 = \sum\limits_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) = \nabla _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) = \nabla _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) \nabla_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 代码, 这个不是很难哦, 网上也有很多, 不在这些了, 私下自己后面再编写吧, 毕竟思路的很清楚了.

BP算法完整推导 2.0 (下)的更多相关文章

  1. BP算法基本原理推导----《机器学习》笔记

    前言 多层网络的训练需要一种强大的学习算法,其中BP(errorBackPropagation)算法就是成功的代表,它是迄今最成功的神经网络学习算法. 今天就来探讨下BP算法的原理以及公式推导吧. 神 ...

  2. BP算法的推导

    反向传播算法的推导 如图为2-layers CNN,输入单元下标为i,数量d:隐层单元下表j,数量\(n_H\):输出层下表k,单元数量c 1.目标 调整权系数\(w_{ji}\),\(w_{kj}\ ...

  3. EM算法-完整推导

    前篇已经对EM过程,举了扔硬币和高斯分布等案例来直观认识了, 目标是参数估计, 分为 E-step 和 M-step, 不断循环, 直到收敛则求出了近似的估计参数, 不多说了, 本篇不说栗子, 直接来 ...

  4. 一文彻底搞懂BP算法:原理推导+数据演示+项目实战(上篇)

    欢迎大家关注我们的网站和系列教程:http://www.tensorflownews.com/,学习更多的机器学习.深度学习的知识! 反向传播算法(Backpropagation Algorithm, ...

  5. 深度学习——前向传播算法和反向传播算法(BP算法)及其推导

    1 BP算法的推导 图1 一个简单的三层神经网络 图1所示是一个简单的三层(两个隐藏层,一个输出层)神经网络结构,假设我们使用这个神经网络来解决二分类问题,我们给这个网络一个输入样本,通过前向运算得到 ...

  6. 多层神经网络BP算法 原理及推导

    首先什么是人工神经网络?简单来说就是将单个感知器作为一个神经网络节点,然后用此类节点组成一个层次网络结构,我们称此网络即为人工神经网络(本人自己的理解).当网络的层次大于等于3层(输入层+隐藏层(大于 ...

  7. BP算法的矩阵推导

    目录 1. 需要的微积分知识 1.1 导数 1.2 求导的链式法则 2. 梯度下降法 2.1 梯度 2.2 梯度算法的解释 3.误差反向传播算法 3.1 理论推导 3.1.1 符号说明 3.1.2 推 ...

  8. 人工神经网络反向传播算法(BP算法)证明推导

    为了搞明白这个没少在网上搜,但是结果不尽人意,最后找到了一篇很好很详细的证明过程,摘抄整理为 latex 如下. (原文:https://blog.csdn.net/weixin_41718085/a ...

  9. 从 0 开始机器学习 - 神经网络反向 BP 算法!

    最近一个月项目好忙,终于挤出时间把这篇 BP 算法基本思想写完了,公式的推导放到下一篇讲吧. 一.神经网络的代价函数 神经网络可以看做是复杂逻辑回归的组合,因此与其类似,我们训练神经网络也要定义代价函 ...

  10. 误差逆传播(error BackPropagation, BP)算法推导及向量化表示

    1.前言 看完讲卷积神经网络基础讲得非常好的cs231后总感觉不过瘾,主要原因在于虽然知道了卷积神经网络的计算过程和基本结构,但还是无法透彻理解卷积神经网络的学习过程.于是找来了进阶的教材Notes ...

随机推荐

  1. java list集合去重的两种方式

  2. 不到24小时,AOne让全员用上DeepSeek的秘诀是……

    DeepSeek引发新一轮AI浪潮,面对企业数字化智能升级与数据安全红线的急迫需求,IT负责人的压力山大!如何在24小时内实现全员AI落地,同时为后续安全部署铺平道路? Step1:一键开启全员智能时 ...

  3. vue - [01] 概述

    题记部分 001 || 什么是Vue   Vue(发音为 /vju:/,类似view)是一款用于构建用户界面的渐进式框架(JavaScript).它基于标准HTML.CSS和JavaScript构建, ...

  4. Archlinux 更新失败之驱动与 Xorg 配置错误

    Archlinux系统更新是滚动更新,所以更新失败又被叫做"滚挂了" 此次滚挂发生在1月27日,过了那么久了才想起来该记录了-- 现象 滚挂的现象是,能够进系统,但是笔记本电脑自带 ...

  5. 大模型本地部署搭建【ollama + deepseek + dify】

    大模型本地部署搭建[在线] 一.ollama的下载.安装.配置 ollama是管理和运行所有开源大模型的平台 下载地址:https://ollama.com/download 或github下载:ht ...

  6. clickhouse--表引擎

    表引擎 表引擎(即表的类型)决定了: 1)数据的存储方式和位置,写到哪里以及从哪里读取数据 2)支持哪些查询以及如何支持. 3)并发数据访问. 4)索引的使用(如果存在). 5)是否可以执行多线程请求 ...

  7. Dify 和 Manus 的技术架构差异

    Dify 框架能够部分实现 Manus 的功能效果,但在复杂任务自动化.多代理协作等领域存在技术差距. 一.核心功能对比 1. 任务拆解与执行能力 Dify:支持通过 Agent 模式 进行任务分解, ...

  8. 【Matlab函数】提取inp文件中的节点、单元数据并保留集合信息

    功能 提取hypermesh2020(其他版本也可以)中的节点.单元信息,并保留elem set信息. 返回的是一个结构体 输入输出参数 输入: inp文件路径,如:'example.inp'.&qu ...

  9. git和github的入门操作

    之前因为工作中用的都是SVN版本控制工具,没接触过git和github,现在开始深入自学Django框架技术后,看到官网推荐使用git,然后这两天网上查阅了很多文章教程,学到入门操作需要学习的点,太多 ...

  10. useDeferredValue的作用

    前言 useDeferredValue是react18新增的一个用于优化性能的一个hook,它的作用是延迟获取一个值,实际开发中的常规用法与我们之前所用的防抖和节流很相似,但是也有一定的区别.本篇文章 ...