揉扁搓圆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/7/25 0:39:21 阅读更多 →
如何为sp500项目贡献代码:社区协作与数据更新流程

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

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

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

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

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

2026/7/21 18:00:15 阅读更多 →

最新新闻

5分钟掌握Umi-OCR:免费离线文字识别的终极解决方案

5分钟掌握Umi-OCR:免费离线文字识别的终极解决方案

5分钟掌握Umi-OCR:免费离线文字识别的终极解决方案 【免费下载链接】Umi-OCR OCR software, free and offline. 开源、免费的离线OCR软件。支持截屏/批量导入图片,PDF文档识别,排除水印/页眉页脚,扫描/生成二维码。内置多国语言库…

2026/7/25 11:52:47 阅读更多 →
Yunjue Agent:动态进化AI助手的架构与实践

Yunjue Agent:动态进化AI助手的架构与实践

1. 项目背景与核心价值上周在测试环境部署了Yunjue Agent的第一个公开版本,这个号称"能够自我进化"的智能助手系统确实让我眼前一亮。不同于传统AI助手需要预设规则和固定流程,这套系统最吸引人的是它的动态学习机制——就像给AI装上了永不停歇…

2026/7/25 11:52:47 阅读更多 →
智能体协作系统在企业数字化转型评估中的应用

智能体协作系统在企业数字化转型评估中的应用

1. 项目背景与核心价值数字化转型评估这个领域最近两年突然火了起来,但市面上大多数评估方法还停留在传统的问卷调查和专家访谈阶段。我们团队在服务了30多家企业的数字化转型咨询后,发现了一个痛点:传统评估方式存在严重的主观性和滞后性。去…

2026/7/25 11:52:47 阅读更多 →
Obsidian手写笔记插件:三步实现PDF自由标注的终极方案

Obsidian手写笔记插件:三步实现PDF自由标注的终极方案

Obsidian手写笔记插件:三步实现PDF自由标注的终极方案 【免费下载链接】obsidian-handwritten-notes Obsidian Handwritten Notes Plugin 项目地址: https://gitcode.com/gh_mirrors/ob/obsidian-handwritten-notes 在数字笔记时代,Obsidian手写笔…

2026/7/25 11:52:47 阅读更多 →
Replit Vibe Coders直播编程:工作量证明与实时协作技术解析

Replit Vibe Coders直播编程:工作量证明与实时协作技术解析

最近在云开发领域,Replit 推出的 "Professional Vibe Coders 工作量证明直播" 引起了广泛关注。作为一名长期关注云端开发工具演进的技术博主,我发现这一创新模式不仅改变了传统编程协作方式,更为开发者提供了全新的技术展示平台。…

2026/7/25 11:52:46 阅读更多 →
智能体技能(Agent Skill)开发指南与应用实践

智能体技能(Agent Skill)开发指南与应用实践

1. Agent Skill的本质解析Agent Skill(智能体技能)本质上是一套可复用的能力模块,它让AI系统具备了完成特定任务的"肌肉记忆"。就像专业厨师不需要每次做菜都重新研究刀工火候一样,经过训练的Agent Skill能让AI在面对同…

2026/7/25 11:51:46 阅读更多 →

日新闻

突破文档下载限制:kill-doc让你看到的都能保存

突破文档下载限制:kill-doc让你看到的都能保存

突破文档下载限制:kill-doc让你看到的都能保存 【免费下载链接】kill-doc 看到经常有小伙伴们需要下载一些免费文档,但是相关网站浏览体验不好各种广告,各种登录验证,需要很多步骤才能下载文档,该脚本就是为了解决您的…

2026/7/25 0:00:35 阅读更多 →
C++ string类模拟实现:从深拷贝到内存管理的完整指南

C++ string类模拟实现:从深拷贝到内存管理的完整指南

1. 项目概述:为什么我们要“手撕”string类?在C的学习道路上,尤其是从C语言过渡到C的“初阶”阶段,string类绝对是一个绕不开的核心。标准库里的std::string用起来太方便了,、find、substr,几个操作符和函数…

2026/7/25 0:00:35 阅读更多 →
三角洲寻宝鼠工具:高效文件搜索与资源管理实战指南

三角洲寻宝鼠工具:高效文件搜索与资源管理实战指南

1. 先搞清楚“三角洲寻宝鼠”到底是什么工具从名称来看,“三角洲寻宝鼠”更像是一个资源查找或文件检索类工具,而不是游戏或娱乐软件。这类工具的核心价值在于帮助用户快速定位特定资源,比如文档、图片、压缩包或特定格式的文件。如果你经常需…

2026/7/25 0:00:35 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/25 5:08:22 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/25 5:13:53 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/24 18:52:18 阅读更多 →

月新闻