揉扁搓圆transformer架构:反向传播算法详解
深度学习一大特色就是能够根据给定数据和自己的预判结果进行自我调整然后让自己的预判结果跟实际数据越来越接近。前面我们研究的损失函数用于判断模型的输出结果与实际结果的“接近层度”反向传播算法就是调整模型内部参数然后让模型的输出在损失函数的评判下使得输出结果与实际数据的“接近层度”越来越大也就是让损失函数的数值越来越小。如果你了解微积分那么你就会了解通过求导来求函数极值。如果一个单变量函数yf(x)我们想要找到x’使得f(x’)是整个函数的最小值那么基本算法是先使用任意一点x0,然后计算在点x0处的导数。如果导数值为0那么此时x0就是函数的极大值或者是极小值到底是哪一种情况我们需要做进一步的判断但无论如何x0一定是函数的极值点。如果在x0处求导后结果大于0那意味着往x0的右边挪动会使得f(x)取值增大,向x0左边挪懂就能让f(x)取值减小由于我们求最小值因此在这种情况下就需要向x0左边挪动“一小步”。同理如果在x0处求导结果小于0那意味着向x0左边挪动“一小步”会让函数取值增大向右边挪动“一小步”就会让f(x)取值减小。这里的“一小步”到底如何量化也是大模型算法的一个要点我们后面会详细研究。反向传播算法的目的之一就是确定“向左”还是“向右”挪动。虽然原理与上面的单变量函数相同但是大模型拥有数千亿乃至万亿参数因此它的处理就要比上面的描述复杂得多我们看看基于大模型反向传播算法的执行步骤1.前向传播。该步骤跟我们前面描述单变量函数类似由于大模型可以看做是一个多变量函数因此输入对应的就是一个含有多个变量的向量通常成为tensor,通过输入数据得到模型的判断结果:F(X).由于大模型是一层连着一层构成的因此前向传播时我们需要逐层计算上一层的输出会成为下一层的输入例如当前层属于第l层那么此时的计算为:其中a(l-1)是上一层的输出结果Wl 表示上一层与当前层参数直接的连接参数。例如第l-1层有10个神经元第l层有20个神经元l-1层的第一个神经元与l层的神经元都有连接那么连接参数就是20个由于l-1层每个神经元都跟l层所有神经元有连接于是这些连接参数就可以使用一个10X20的矩阵来表示这些参数就对应要求导确定调整方向的参数。在计算完当前层的输出结果后我们还需要使用激活函数对当前输出做一次非线性变化:对于大模型而言上面使用的激活函数f通常是GeLU。当计算传导到最后一层我们获得最后一层的输出结果后就能将输出结果和实际要对比的数据传入损失函数进行计算由此得到模型输出跟实际结果的“差距”。2.反向传播求导。模型内部可调整的参数其实就是模型内部一层层神经元前后连接的参数。前面我们看到前后两层神经元之间的连接参数可以使用矩阵来表示因此我们把模型看做一个函数的话那么结合内部参数就可以表达成 L F(W1, W2…Wn)。反向传播求导的核心原则是链式法则加深损失L依赖于输出a, 同时a又通过参数w可以调整控制那么要调整L就需要调整a但调整a又得调整w于是根据链式法则就有:对于大模型而言这条反向传播的链条非常长。正如夜长梦多一旦链条长了出错的概率就很大后面我们会看到如何使用一系列算法控制这个长链条计算过程中出现的偏差。我们针对当前第l层来拆解具体的数学步骤2.1.计算当前层的输出误差流如果我们当前位于第l层由于是反向传播那么我们就已经知道l1层反向传回来的梯度数值d^(l1).从前面前向传播的过程我们知道第l1层的输入来自于第l层的输出进过一次激活函数。因此当前l层的梯度计算就是上图中符号“一个圆圈内部一个点”表示两个向量逐元素相乘。这一步叫“将误差通过激活函数反向传播”2.2.由于两层神经元直接的连接参数就是模型内部要修改的关键参数因此我们需要针对连接参数W进行修正对于权重W^l也就是第l层于第l1层直接的连接参数它的修改步骤如下:每一层除了连接参数外还有一个针对该层的修正参数b^l,我们也要针对其进行修正:2.3.我们需要将梯度继续向前传播也就是从第l层向第l-1成传播以便修正第l-1层连接的参数3.利用反向传播算出来的梯度使用各种优化器SGD, Adam更新参数:上面描述的算法步骤包含多个数学参数看起来很乱我们走一段具体代码来理解上面的算法步骤。我们将使用代码模拟一个两层神经网络每层只包含两个神经元然后走一遍向前传播和向后传播的过程执行上面的算法步骤。首先我们先设计一个拥有两层每层两个节点的网络:importnumpyasnp #设置随机种子np.random.seed(42)#网络结构与参数# 输入特征2个样本每个样本2维特征Xnp.array([[0.5,0.3],[0.2,0.8]])# shape:(2,2)批量大小2Y_truenp.array([[0.8,0.2],[0.6,0.4]])# 真实标签 shape:(2,2)# 第一层输入-隐藏:2个输入2个神经元W1np.random.randn(2,2)*0.5#shape(2,2)b1np.random.randn(2,1)*0.5#shape(2,1)# 第二层隐藏-输出:2个输入2个神经元W2np.random.randn(2,2)*0.5#shape(2,2)b2np.random.randn(2,1)*0.5#shape(2,1)# 激活函数sigmoid defsigmoid(z):return1/(1np.exp(-z))defsigmoid_derivative(a):returna*(1-a)上面代码对应的网络结构如下:从上图我们可以看出输入 W^1 [ [0.25,0.32],[-0.07, 0.76]], b1[-0.12,-0.12]; W^2[[0.79,-0.23],[0.38,0.27]], b2[-0.23, 0.23]. 接下来我们看看如何执行前向传播:print( 前向传播 )# 输入层激活值 a0X.T#shape(2,2)每一列是一个样本 # 第一层线性变换 z1np.dot(W1,a0)b1 #shape(2,2)a1sigmoid(z1)#shape(2,2)# 第二层线性变换 z2np.dot(W2,a1)b2 #shape(2,2)a2sigmoid(z2)#shape(2,2)#损失函数均方误差(MSE)Lnp.mean((a2-Y_true.T)**2)print(fa0 (输入激活值):\n{a0}\n)print(fz1 (隐藏层线性输入):\n{z1}\n)print(fa1 (隐藏层激活值):\n{a1}\n)print(fz2 (输出层线性输入):\n{z2}\n)print(fa2 (输出层激活值):\n{a2}\n)print(fL (损失): {L:.6f}\n)上面代码运行后给出结果如下:前向传播a0(输入激活值):[[0.50.2][0.30.8]]z1(隐藏层线性输入):[[-0.01363779-0.12271099][0.273308130.55691232]]a1(隐藏层激活值):[[0.49659060.46936069][0.567904870.63573781]]z2(输出层线性输入):[[0.378317240.382845][-0.19537192-0.17057832]]a2(输出层激活值):[[0.593467180.5945591][0.451311790.45745852]]L(损失):0.027286通过上面代码我们可以看到a1其实是a0经过第一层神经元与第二层神经元的链路上的参数在加上链路末尾的调整参数b1后的结果z1是输入第二层神经元时执行一次激活函数后的结果。我们通过如下图像能比较好的理解这个过程:从上图可以看到输入信号a0[0] 0.5,经过第一层第一个节点与第二层第一个节点的链路后用输入信号乘以链路权重也就是 0.50.25,然后第一层第二个元素的输入值a0[1]0.3这个数值经过第一层第二个元素与第二层第一个元素的链路后信号乘以链路上的参数也就是0.3-0.15, 两个输入到第二层第一个元素的信号数值加总再加上第二层第一个神经元的调整参数b0[0]-0.12于是得到输入第二层第一个元素的加总信号值为 z1[0] 0.5 * 0.25 0.3 * -0.15 -0.12 0.125, 这个数值在经过一次激活变成第二层第一个元素的输出信号a1[0]也就是a1[0] sigmoid(z1[0]) 0.479,第二层第二个神经元的输出数值可以此类推。接下来我们看看反向传播的算法原理。反向传播其实是链式法则求导的具体实现。从前向传播我们可以看到如下的逻辑链条:从上面路径可以看到最终损失函数的计算中链路参数W0[0][0]发挥了作用。我们想要判断如何调整W0[0][0]才能让损失函数的结果降到最小那么就需要基于损失函数来针对W0[0][0]求导。问题在于W0[0][0]并不是直接作用于损失函数而是通过层层迭代传递来影响损失函数。W0[0][0]先促进Z1[0]也就是第二层第一个神经元输入信号的数值的创建(Z1[0]中的1表示神经网络第2层由于网络层的下标计数以0开始最开始那层为第0层所以1表示第二层网络层其中的0表示第一层中第1个神经元),然后第二层第一个神经元将输入信号数值经过激活函数sigmoid转换后成为它的输出信号值a1[0]最后这个值参与了损失函数的计算。将上面前向传播的链路转换为数学公式如下:上面公式中有:它对应的就是激活函数sigmod然后const对应的就是b0[0],也就是调整常量。在使用反向传播时首先针对最外层的a1[0]也就是第二层第一个神经元的输出信号进行求导:由于a1[0]是第一层两个神经元的输出信号经过对应链路抵达第二层神经元的信号加总后所得的z1[0]经过激活函数sigmoid后所得的结果因此a1[0]是z1[0]函数的结果于是针对z1[0]在基于sigmoid函数上进行求导:最后Z1[0]的形成有一部分是第一层第一个神经元输出的信号a0[0]经过链路权重W0[0]后贡献的(第一层第二个神经元输出的信号a0[1]也通过链路权重W0[1]进行了贡献只是我们只查看W0[0]的偏导所以可以忽略它).于是z1[0]又是W0[0]的函数结果于是基于z1[0]的基础上针对w0[0]进行求导:最后我们把这三部分相乘就得到损失函数针对参数w0[0]这点取值上的导数:上面结果就是损失函数针对参数w0[0]处切线的方向,要想通过调整w0[0]来降低损失函数的结果那么就需要根据这个方向反向调整w0[0]的数值这里的“反向调整”里面的文章大了去了也是后续我们需要特别深入详解的地方在这里我们先简单的认为反向调整就是乘以一个预先固定的参数也叫学习率即可最后我们看反向传播的代码实现:print( 反向传播 )#1.输出层误差 δ2# dL/da2(a2-Y)(因为MSE导数)dL_da2(a2-Y_true.T)#shape(2,2)# f(z2)sigmoid_derivative(a2)delta2dL_da2*sigmoid_derivative(a2)#shape(2,2)print(fδ2 (输出层误差, dL/dz2):\n{delta2}\n)#2.输出层参数梯度 dW2,db2 # dW2δ2*a1^TdW2np.dot(delta2,a1.T)#shape(2,2)# db2sum(δ2,axis1,keepdimsTrue)# 对样本维度求和 db2np.sum(delta2,axis1,keepdimsTrue)#shape(2,1)print(fdW2 (输出层权重梯度):\n{dW2}\n)print(fdb2 (输出层偏置梯度):\n{db2}\n)#3.隐藏层误差 δ1# dL/da1W2^T*δ2dL_da1np.dot(W2.T,delta2)#shape(2,2)# f(z1)sigmoid_derivative(a1)delta1dL_da1*sigmoid_derivative(a1)#shape(2,2)print(fδ1 (隐藏层误差, dL/dz1):\n{delta1}\n)#4.隐藏层参数梯度 dW1,db1 dW1np.dot(delta1,a0.T)#shape(2,2)db1np.sum(delta1,axis1,keepdimsTrue)#shape(2,1)print(fdW1 (隐藏层权重梯度):\n{dW1}\n)print(fdb1 (隐藏层偏置梯度):\n{db1}\n)#参数更新演示learning_rate0.1W1_newW1-learning_rate*dW1 b1_newb1-learning_rate*db1 W2_newW2-learning_rate*dW2 b2_newb2-learning_rate*db2print( 参数更新 )print(更新后的 W1:\n,W1_new)print(更新后的 b1:\n,b1_new)print(更新后的 W2:\n,W2_new)print(更新后的 b2:\n,b2_new)可以仔细对应上面代码和前面的逻辑分析就能看到每一步实现都对应前面描述的链式法则。上面代码运行后给出的结果如下:反向传播δ2(输出层误差,dL/dz2):[[-0.04982891-0.00131158][0.06223220.01426064]]dW2(输出层权重梯度):[[-0.02536017-0.0291319][0.037597310.044408]]db2(输出层偏置梯度):[[-0.05114049][0.07649285]]δ1(隐藏层误差,dL/dz1):[[-0.01348773-0.00109167][-0.000549150.00077933]]dW1(隐藏层权重梯度):[[-0.0069622-0.00491965][-0.000118710.00045872]]db1(隐藏层偏置梯度):[[-0.0145794][0.00023019]]参数更新更新后的W1:[[0.2490533-0.06864019][0.323856140.76146906]]更新后的 b1:[[-0.11561875][-0.1170915]]更新后的W2:[[0.792142420.38663055][-0.238496920.26683922]]更新后的 b2:[[-0.2265948][-0.24051416]]

相关新闻

OptiScaler v0.7.7-pre8:游戏图像优化终极指南与实战技巧

OptiScaler v0.7.7-pre8:游戏图像优化终极指南与实战技巧

OptiScaler v0.7.7-pre8:游戏图像优化终极指南与实战技巧 【免费下载链接】OptiScaler OptiScaler bridges upscaling/frame gen across GPUs. Supports DLSS2/XeSS/FSR2 inputs, replaces native upscalers, enables FSR-FG/XeFG on non-FG titles. Supports Nukem…

2026/9/22 12:12:14 阅读更多 →
如何为sp500项目贡献代码:社区协作与数据更新流程

如何为sp500项目贡献代码:社区协作与数据更新流程

如何为sp500项目贡献代码:社区协作与数据更新流程 【免费下载链接】sp500 Current and Historical Lists of S&P 500 components since 1996 项目地址: https://gitcode.com/gh_mirrors/sp/sp500 欢迎来到标普500指数数据项目!📊 …

2026/9/22 12:51:43 阅读更多 →
别再手动列提纲了!3类高价值场景下AI大纲生成的精准度校准指南(含评估量表V2.1)

别再手动列提纲了!3类高价值场景下AI大纲生成的精准度校准指南(含评估量表V2.1)

更多请点击: https://codechina.net 第一章:别再手动列提纲了!3类高价值场景下AI大纲生成的精准度校准指南(含评估量表V2.1) 在技术文档、产品需求与学术写作三大高频场景中,AI生成的大纲常因目标模糊、领…

2026/9/22 8:20:58 阅读更多 →

最新新闻

2026最新Nyan Cat项目配置避坑:5个报错一次讲透

2026最新Nyan Cat项目配置避坑:5个报错一次讲透

2026最新Nyan Cat项目配置避坑:5个报错一次讲透 刚接手那个老项目的同事,是不是也被 Nyan Cat 这个前端特效卡得怀疑人生?明明只是加个彩虹猫跑马灯,结果 npm install 还没跑完, webpack 直接报…

2026/9/22 12:51:39 阅读更多 →
遥感信息处理避坑指南:3个完整示例搞定API变更

遥感信息处理避坑指南:3个完整示例搞定API变更

遥感信息处理避坑指南:3个完整示例搞定API变更 版本升级后 API 全变了,是不是让你抓狂?刚写好的脚本跑不起来,报错信息看得头大。别慌,我整理了遥感信息处理的完整示例,帮你快速上手。…

2026/9/22 12:51:39 阅读更多 →
5步搞定无限的未知win7性能瓶颈,实战项目提速3倍

5步搞定无限的未知win7性能瓶颈,实战项目提速3倍

5步搞定无限的未知win7性能瓶颈,实战项目提速3倍 官方文档翻了三遍还是晕?别慌,很多老手都卡在这。无限的未知win7这种底层机制,光看理论根本跑不起来。拿一个 实战项目 实测,你才会发现哪里在拖后腿。…

2026/9/22 12:51:39 阅读更多 →
3个坑让你避开天正建筑8.5免费下载陷阱,面试必问的选型逻辑

3个坑让你避开天正建筑8.5免费下载陷阱,面试必问的选型逻辑

3个坑让你避开天正建筑8.5免费下载陷阱,面试必问的选型逻辑 版本升级后 API 全变了,代码直接报错,这是很多老架构师深夜修 Bug 时的真实写照。天正建筑 8.5 作为 Autodesk 平台上的经典插件,其底层调用机制在…

2026/9/22 12:51:39 阅读更多 →
一文搞懂一一一一

一文搞懂一一一一

3个坑搞定Java线程池,一文搞懂性能调优 官方文档里关于 ThreadPoolExecutor 的参数说明长达几十页,全是术语堆砌,初学者往往看完只觉得头晕,根本抓不住重点。 别慌,今天我们就用 一文搞懂 的方式,把 Java…

2026/9/22 12:51:39 阅读更多 →
vue开发工具图解原理:3步搞定环境配置不再卡半天

vue开发工具图解原理:3步搞定环境配置不再卡半天

vue开发工具图解原理:3步搞定环境配置不再卡半天 装个Vue开发环境,npm install 报错、版本不兼容、浏览器白屏,配置半天没跑起来?别急,今天带你用图解原理的方式,把 vue开发工具…

2026/9/22 12:50:39 阅读更多 →

日新闻

3台商务办公笔记本实测:手写实现环境配置,告别卡半天

3台商务办公笔记本实测:手写实现环境配置,告别卡半天

3台商务办公笔记本实测:手写实现环境配置,告别卡半天 配置环境就卡半天?别怪机器慢,多半是你没选对工具链。在Java、Go或Python的项目现场, 手写实现…

2026/9/22 0:00:41 阅读更多 →
剑帝加点速查手册:3分钟搞懂核心逻辑

剑帝加点速查手册:3分钟搞懂核心逻辑

剑帝加点速查手册:3分钟搞懂核心逻辑 面试被问原理答不上来,是不是常态?别慌。很多开发者对着 GitHub 开源仓库里的代码发呆,看似简单实则暗藏玄机。今天这份【剑帝加点】速查手册,直接带你拆解核心实现,把面试必考的原理讲透。…

2026/9/22 0:00:41 阅读更多 →
手写实现图片压缩网站核心:搞定WebP转换与质量调优

手写实现图片压缩网站核心:搞定WebP转换与质量调优

手写实现图片压缩网站核心:搞定WebP转换与质量调优 复制来的代码跑不通不知道怎么调?别慌,这种“复制粘贴地狱”在开发圈太常见了。尤其是做 图片压缩网站…

2026/9/22 0:00:41 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/22 4:32:41 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/22 4:38:57 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/22 8:51:04 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/21 15:36:51 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/21 15:36:51 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/22 2:43:42 阅读更多 →