家人们,我准备开启一个全新的系列,来聊聊——神经网络的原理与实现。
也许你已经听过“神经网络”这个词无数次,它是深度学习的基石,是 ChatGPT、图像识别、自动驾驶背后的关键技术。但你是否真正理解过,神经网络到底是怎么“看懂”图像、听懂语言,甚至学会写代码的?这些“看似智能”的行为,背后到底发生了什么?
这个系列将从最基础的多层感知器(MLP)出发,一步步揭开神经网络的神秘面纱,带你理解:
● 神经网络到底是什么?它是如何“模仿人脑”的?
● 什么是梯度下降?为什么梯度下降能优化模型?
● 什么是反向传播?
● 如何用代码手写一个简单的神经网络,从0开始训练识别手写数字?
这个系列会兼顾原理解释与工程实现,面向所有对 AI 感兴趣的朋友,不管你是小白入门,还是进阶学习,相信都能有所收获。
此外,所有相关源码示例、流程图、模型配置与知识库构建技巧,我也将持续更新在Github:LLMHub,欢迎关注收藏!
本篇文章是该系列的第三篇,我们一起来学习一下神经网络学习背后的核心算法——反向传播算法,即使现在很多人都在做深度学习,但是80%的人都说不清网络的参数到底是怎么更新的,所以如果你搞懂了反向传播算法,那么你就已经超越了大多数人了。
仍然以前面文章中用到的MLP
为例,假设我们的网络还没有经过训练,所以输出会非常随机,可能是0.5,0.8等等,我们不能直接改变这些激活值,只能改变权重和偏差,我们希望网络输出是2,所以我们希望第三个值增大,而其他所有值减小,每个值变动的幅度应该和每个值与其目标值的距离成比例。
进一步的放大,我们只关注第二个神经元,我们希望增加他的激活值。注意一下,激活值在前文中被定义为前一层中所有激活的某个加权和加上偏置,然后放到激活函数中得到的目标值。
所以三种不同的途径可以增加这个神经元的激活值
- 增加偏置b增加权重w改变上一层的激活值
如果目标神经元前一层所有具有正权重的神经元都变得更亮(激活值变大),具有负权重的神经元都变得更暗(激活值变小),那么目标神经元就会变得更活跃,当然我们不能直接影响神经元中的激活值,我们只能改变权重和偏置来达到这个目标。但是记住每一个神经元该怎么变化是有用的。
上面是仅针对一个神经元,但是最后一层的每个神经元对于倒数第二层的每个神经元应该怎么变化都有自己的想法,所有神经元的想法加在一起就会得到倒数第二层所有神经元的期望变化趋势。重复的递归这个过程,也就得到了网络中所有神经元的期望变化趋势,这就是反向传播的核心思想。
在相关成本函数表面绘制更新轨迹,它更像一个醉汉漫无目的跌跌撞撞的走下山坡,但迈着快步,而不是一个经过精心计算的人 ,确定每一步的确切下坡方向,然后朝着那个方向迈出非常缓慢而小心的一步,这种技术也就是随机梯度下降。
反向传播算法是确定每个单个训练示例如何调整权重和偏置的算法,不仅决定他们应该是增大还是减小,还决定每一个参数应该改变的幅度来实现成本最快下降。
掌握了什么是反向传播算法后,接下来我们开始学习在方向传播算法中怎么更新这些参数。
我们从一个非常简单的网络开始,其中每一层都只有一个神经元,所以这个网络由三个权重和三个偏置决定,我们的目标是了解成本函数对这些参数的敏感度,我们也就能了解怎么进行修改参数能实现成本函数最有效的下降。
我们首先关注最后两个神经元,我们用a<sup>(L)</sup>
表示最后一个神经元的激活值,L表示位于哪一层,所以前一层的激活值就是a<sup>(L-1) </sup>
,假设我们期望的激活值是y,因此对于单个训练示例,该网络的成本是C<sub>0</sub> = (a<sup>(L) </sup>- y)<sup>2</sup>
我们知道激活值的计算公式如下:
我们将括号中的加权值提取出来用变量z<sup>(L)</sup>
表示,并且上标和激活值上标相同。
可以看到权重和上一层的激活值和偏置一起计算得到z<sup>(L)</sup>
,进而计算a<sup>(L)</sup>
,最后与y计算C<sub>0</sub>
。
所有的这些都是数字,我们首先要了解成本函数对权重w<sup>(L)</sup>
变化的敏感度,也就是C对于w<sup>(L)</sup>
的导数是什么,也就是<font style="color:rgba(0, 0, 0, 0.8);">δC</font><sub><font style="color:rgba(0, 0, 0, 0.8);">0</font></sub><font style="color:rgba(0, 0, 0, 0.8);">/δw</font><sup>(L)</sup>
,其中<font style="color:rgba(0, 0, 0, 0.8);">δw</font><sup>(L)</sup>
可以看作对<font style="color:rgba(0, 0, 0, 0.8);">w</font><sup>(L)</sup>
的微小推动,例如改变0.01,<font style="color:rgba(0, 0, 0, 0.8);">δC</font><sub><font style="color:rgba(0, 0, 0, 0.8);">0</font></sub>
表示对成本产生的推动,我们要的是他们的比例,从图中可以看到,对<font style="color:rgba(0, 0, 0, 0.8);">w</font><sup>(L)</sup>
的微小推动会导致<font style="color:rgba(0, 0, 0, 0.8);">z</font><sup>(L)</sup>
的推动,进而导致对<font style="color:rgba(0, 0, 0, 0.8);">a</font><sup>(L)</sup>
的推动,从而直接影响成本。
也就得到了
这就是大名鼎鼎的链式法则
由于完整的成本函数涉及对许多不同的n
个训练示例的所有成本进行平均,因此其导数需要对所有训练示例对该表达式进行平均。
当然,这也只是梯度向量中的一个分量,梯度向量是由成本函数针对所有权重和偏置的偏导数构成的。
尽管我们不能直接影响神经元中的激活值,但跟踪他还是有帮助的,因为我们可以继续向后迭代相同的链式法则思想,来查看成本函数对前面权重和偏置的敏感程度。
对于复杂的神经网络,他们的本质是相同的
不同之处是神经元可以通过多条不同的路径影响成本函数,所以导数就要进行相加,之后不断进行迭代得到每一个参数的期望变化。
到这里我们已经学习了反向传播算法的核心内容,这些链式法则表达式为我们计算梯度中每个组成部分的导数,得到梯度之后我们就可以进行正向参数更新,通过不断地训练迭代来帮助最小化成本。
那我们怎么实现参数的更新呢?答案是梯度下降,没错,就是我们前一篇文章介绍的梯度下降。
其中η是一个较小的正参数(称为学习率),目的是避免参数更新过快或者过慢。之后就是不断迭代进行参数更新,直到参数更新完毕,网络则训练完成。
码字不易,如果对你有一点帮助,点个喜欢吧!
关于深度学习和大模型相关的知识和前沿技术更新,请关注公众号算法coting
!
以上内容参考
非常感谢,如有侵权请联系删除!