家家人们,我准备开启一个全新的系列,来聊聊——神经网络的原理与实现。
也许你已经听过“神经网络”这个词无数次,它是深度学习的基石,是 ChatGPT、图像识别、自动驾驶背后的关键技术。但你是否真正理解过,神经网络到底是怎么“看懂”图像、听懂语言,甚至学会写代码的?这些“看似智能”的行为,背后到底发生了什么?
这个系列将从最基础的多层感知器(MLP)出发,一步步揭开神经网络的神秘面纱,带你理解:
● 神经网络到底是什么?它是如何“模仿人脑”的?
● 什么是梯度下降?为什么梯度下降能优化模型?
● 什么是反向传播?
● 如何用代码手写一个简单的神经网络,从0开始训练识别手写数字?
这个系列会兼顾原理解释与工程实现,面向所有对 AI 感兴趣的朋友,不管你是小白入门,还是进阶学习,相信都能有所收获。
此外,所有相关源码示例、流程图、模型配置与知识库构建技巧,我也将持续更新在Github:LLMHub,欢迎关注收藏!
我们人类能够轻松识别模糊、低分辨率的数字,但这对计算机来说却并不容易。神经网络的出现使得这类任务成为可能。本文将以手写数字识别为例,介绍最简单也是最经典的神经网络模型 —— 多层感知器(MLP, Multi-Layer Perceptron)。理解了 MLP,我们才能更好地理解后续更强大的现代神经网络。
一幅图像由若干像素构成,例如下图就是一个 28×28 的灰度图,总共包含 784 个像素。若想识别图像中的内容,最自然的方式就是从这些像素出发。
神经网络,其中的神经代表神经元,我们可以把它理解为一个用来装数字的容器,神经元中的数字代表对应像素的灰度值,0表示纯黑像素,1表示纯白像素,我们把神经元里装的数叫做“激活值”, 我们可以把激活值想象成神经元的“亮度”,值越大,就越“亮”。
网络第一层有784个神经元,分别对应图片中的每一个像素,最后一层有9个神经元,分别代表数字1-9,也就是我们要分类的目标种类数,最后一层的激活值也是在0-1之间,代表着属于类别的概率值。
除了输入层和输出层,中间还有若干“隐藏层”。每一层神经元的激活值,都是根据前一层的激活值计算出来的。也就是说,信息在网络中是逐层传递的,每一层都在提取更抽象的特征。
神经网络模拟了人类大脑的处理方式:某些神经元被激活,会“促使”下一层某些神经元也被激活。输入图像中某种特定的激活模式,会逐层转换成新的激活模式,最终在输出层形成判断结果。输出层中激活值最大的神经元,就代表网络的最终预测。
当我们识别数字时,常常是将数字的各个部分进行组合。例如,数字 9 可以理解为“一个圈 + 一个竖”,8 是“上圈 + 下圈”,4 是“两竖 + 一横”。
理想情况下,我们希望神经网络的倒数第二层能够学习到这些“部件”,例如圈、竖、横等。这样在输入数字 8 或 9 时,对应“圈”神经元的激活值就会升高,最后一层只需学会哪些部件组合出哪个数字即可。
但“圈”也不是凭空得来,它可以由多个短边组成。
所以我们希望网络的第二层神经元就能对应上这些各种各样的短边,当输入数字图像进行计算时,网络就能把所有关联短边的若干个神经元都给点亮,接着就能点亮网络第三层中圆圈或者竖组件对应的神经元,最后就能点亮最后一层对应9的神经元
现在原理很形象的介绍清楚了,那么神经网络具体是怎么实现的呢,答案是通过参数的调整,每一个神经元都与上一层的所有神经元连接,每一条线都有一个权重值w。
将每一个神经元中包含的激活值和相对应的权重相乘再相加,可以把权重值看成一个表格,其中绿色为正的权重值,红色为负的权重值,颜色越暗表示权重越接近于0,如果把关注区域的权重设为1,其他所有的权重值全设为0,这样对所有像素值取加权和,就把注意力聚焦在我们关注区域的像素值了。
但是加权和算出来的数值可能是任意大小,而网络中的激活值要求在0-1之间,这个时候我们就要把数值放缩到0-1之间,也就是我们常听到的激活函数。
其中最经典的就是sigmoid函数,简而言之,他能把非常大的负值变成接近0,把非常大的正值变成接近1,而在取值0附近则是均匀增长的。
所以神经元中的激活值实际上就是一个对加权和到底有多正的打分,但有时,即使加权和大于0时,你也不想把神经元点亮,可能只有当区域内的神经元加权和大于10的时候才想点亮,此时就需要加一个偏置值,避免随便激活,只需要在函数之后减去10,然后送到sigmoid函数即可,总而言之,权重告诉你第二层的神经元关注什么样的像素图案,偏置则告诉你加权和得有多大,才能让神经元的激活变得有意义。
上面是一个神经元,一层中的每一个神经元,都会和第一层全部的784个神经元相连接,每一条连线上都戴着一个权重,而且每一个神经元在计算自己的加权和后都会加上一个偏置,再通过sigmoid压缩结果,第二层的16个神经元就需要计算784*16个权重值和16个偏置值。
一整个网络一共会用到13002个参数,相当于这个网络上有13002个“旋钮开关” 让你调整,从而带来不一样的结果,所以网络在训练的时候实质上就是在学习如何设置这些数字参数。
在计算机计算的过程中,这些参数是不可能一个一个罗列在代码中的,我们把某一层的所有激活值统一成一列向量,再把他和下一层之间的所有权重放到一个矩阵中,矩阵中的第n行就代表所有激活值和下一层第n个神经元之间所有连线的权重,这样权重矩阵和向量乘积的第n项,就是这一层的所有激活值和下一层的第n个神经元之间连线权重的加权和。
同样,偏置我们也放在一个向量里,然后与之前矩阵乘法的结果相加,最后用sigmoid把整个表达式包起来进行运算,这样就可以简洁明了的表示网络之间进行的计算,这种表达也使我们写程序变得简便了许多,因为存在很多对矩阵操作的库函数和框架来简化编码。
还记得刚开始我们把一个神经元看作一个数字的容器吗?实际上神经元中装着的值是取决于你的输入图像的,所以我们把神经元看成一个函数才更加准确,他的输入是上一层所有的神经元的输出,输出是一个0-1的值。
其实一整个神经网络同样也是一个函数, 输入是一个包含 784 个值的向量(图像像素),输出是一个包含 10 个值的向量(数字概率), 不过这个函数极其复杂,用了13002个权重参数和偏置参数来识别特殊图案,又要循环的用到矩阵乘法和sigmoid映射计算。虽然复杂,但它的每一个组成部分都可以被理解和解释。
关于深度学习和大模型相关的知识和前沿技术更新,请关注公众号算法coting!
以上内容参考
But what is a neural network? | Deep learning chapter 1
非常感谢,如有侵权请联系删除!