01 从函数到神经网络

deepseek最近火出圈了
不过可能不少人有这样的两难困境:既不想一直看一些几分钟的快餐视频.因为不论看多少,很多问题还是搞不明白,但是又不想花太多时间从头开始学,因为整个AI的知识体系实在是太庞大了。那么我们就好好的了解一下
我们直接进入正题吧
现在你要做的唯一一件事就是清空大脑
忘掉所有你曾经熟悉的或不熟悉的概念
然后在你脑海中就只留我们这趟旅行的出发点:函数
后面所有一切的前提是你要相信这个世界上的所有逻辑或知识都可以用一个函数来表示
“Functions Describe The world~ "
那我们只需要将现实世界抽象为符号,再设置好一些运算规则,也就是函数最后算出来结果,反过来解释现实世界就可以了,比如说输入直角三角形的两个边长,根据勾股定理就可以得到斜边的边长,再比如输入物体的质量和加速度
根据牛顿第二定律就可以得到物体施加的力,这就是人工智能早期的思路:符号主义

但这条路走到头了,很多问题,人类实在是想不出怎么写成一个明确的函数
从上帝视角看,就是人类还是太菜了,比如说一个简简单单的识别一张图片是否是猫
对人类来说可能简单到爆炸,但是要让计算机运行一段程序来识别,那一下子就变成了一个史诗级难题,就连有着明确语法规则和词典的翻译函数,尚且没有办法做到足够丝滑,那更别说复杂多变的人类智能了,那既然不知道这个函数长什么样,怎么办呢,那就别硬找了,换个思路
我们先从一个简单的例子入手,比如我们知道一些X和Y的值(X:1 2 3 Y:2 4 6),我们想找到Y和X的函数关系,你有什么办法呢,有人说,这不就是Y等于2X吗,傻子都能看出来没错,这就是符号主义的思想
觉得世间万物都能找到背后明确的规律
但假如我们一开始没有找到这个规律,怎么办呢?
比如说下面这组数字就不能一眼看出来
那就用人类有史以来最具智慧的办法猜
我们先把这个XY放到坐标轴上,先随便猜一下,比如说函数关系就是Y等于X
也就是这里的W和B分别是一和零,然后我们一点点调整这个W和B
使得这条直线越来越贴近真实数据
最后呢发现完全吻合了,行就它了
但有的时候可能很难找到完全吻合的函数
那可怎么办呢?
没事那就简化一下问题
大差不差,能近似就行了,别要求那么多
我们的做法仍然是一点一点调整,W和B看差不多的时候就停下来,这就是现代人工智能的思路:猜和简化问题
说白了实际上就是人类摆烂了,承认自己太菜了,找不到精确的函数了,那就找一个从结果上看大差不差的函数
然后连蒙带猜,逐渐逼近真实答案就好了,这种区别于早期人工智能符号主义的新思想叫做连接主义
我们不再追求找到那个精确的函数关系,而是通过简化函数,并试图寻找一个足够接近真实答案的近似解
有人说这连蒙带猜的靠谱吗?一看就不是什么正路子。没错!
在连接主义成为主流之前
很多人工智能的专家也是这么想的
但就是这样靠连蒙带猜的办法
我们居然可以用很少的参数
轻松实现手写数字识别这样的任务
正是这种方式在很多地方证明了它的有效性
人们才开始重视起来
回到正题
刚刚我们举的例子都比较简单
只用直线方程就可以表示了
但假如数据稍稍变化一下
就会发现
不论怎么调整
这里的W和B好像都无法接近真实的数据
那这个时候就需要让这条直线弯一弯了
那换句话说
就是我们需要从原来的线性函数
进化到非线性函数了
那我们就来研究一下
怎么把原来这个原本线性的函数
变成非线性的呢
很简单
在这个函数最外层
再套一个非线性的运算就可以了
比如平方 比如sin 比如e
这就是激活函数
它的目的 就是把原本死气沉沉的线性关系给盘活了
变成了变化能力更强的非线性关系嗯
听到非线性关系的同学
千万不要害怕
常用的激活函数都简单到爆炸 但是就是能起到很好的效果
好了回到这个新的函数形式
我们之前仅仅有一个输入的变量
就是X
但实际上呢可能有很多输入
所以这里的每一个X都要对应一个W
像这样
再者呢有的时候只套一层激活函数
还是没有办法达到很好的效果
也就是说这个曲线弯的还不够灵活

image-20250521202432295

那这要怎么办呢
很简单
我们把刚刚这一大坨当做一个整体
在此基础之上再进行一次线性变换
然后再套上一个激活函数
这样就可以无限的套娃下去了
那通过这样的方式
我们就可以构造出非常非常复杂的线性关系
而且理论上可以逼近任意的连续函数

image-20250521202516116

当然了
这样写下去实在是太让人头大了
普通人看个两层
估计脑子就炸了
所以我们得换一种更傻瓜的更直观的形式
那回到最初的形式
我们把这样一个线性变换
套一个激活函数化成下面这样

image-20250521202555194

左边是输入层
只有一个输入X
右边是输出层
只有一个输出Y
我们把这里的每一个小圈圈叫做一个神经元
当然这里我不建议你把它跟生物的神经元
先类比
因为他们两个其实一毛钱关系都没有
看似很形象
但实际上反而会影响理解
总之就是这样
两个圈圈一连就表示上面一个函数关系
那刚刚我们说输入可能有多个
所以对应的变化就是输入层变成了多个
像这样
我们还说可以继续在外层不断的套线性变换
再套激活函数
那么每套一层就相当于神经元水平方向
又扩展了一个
当然扩展之后呢
中间这一层就不再是最终的输出了
而是包裹在了一个很复杂的函数变换之中
看不到
我们管它叫做隐藏层
而整个这一大坨神经元互相连接
形成的网络结构就叫做神经网络

image-20250521202651748

接下来我们看一下函数和神经网络的对应关系
首先有两个输入变量
一个是X1
另一个是X2
它们构成了输入层
然后X1X2进行一次线性变换
再进行一次激活函数
就得到了隐藏层a
这个a对应的就是上面这一大坨表达式
那我们把它当做一个整体
继续进行一次线性变换和一次激活函数
这就计算出了最终的输出层y
重新再看一下这个过程
从神经网络的这个图来看的话
似乎就像是一个信号
从左到右传播了过去
那这个过程就叫做神经网络的前向传播

image-20250521202743469

但是实际上呢就是一点点分步骤
把一个函数的值计算出来了而已
神经网络的每一层神经元都可以无限增加
同时呢隐藏层的层数也可以无限增加
那进而就可以构成一个
非常非常复杂的非线性函数了
虽然这个函数可能非常复杂
但是我们的目标却非常简单和明确
就是根据已知的一组X和Y的值
猜出所有这里的W和B都是多少

image-20250521202823501

当然了
我们一开始举的例子非常简单
光靠肉眼法就能慢慢猜出答案了
但是现在有这么多参数可能就无法凭感觉猜了
那这要怎么办呢
欲知后事如何
且听下回分解

回顾一下这个内容非常简单
我们从一个最开始的信念函数开始讲起
早期的人工智能
相信可以找到精确的函数来表示一切
但因为这个世界实在太复杂了
所以人们就放弃了转向寻找一个足够接近真实答案的近似解
那我们通过寻找一个线性关系来举例,如何去猜测W和B的值
后来发现线性关系太过简单,不足以描述更复杂的关系,于是引入了非线性的激活函数
通过线性变换和非线性激活函数的不断组合
和套娃可以表达很复杂的关系,但是呢写成函数看太恶心了,所以就化成了神经网络这种形式
那恭喜你
从函数到神经网络的这条路已经被你搞懂了
剩下的所有乱七八糟的知识
都仅仅是为了算出这个W和B而已

02 如何计算神经网络的参数

我们直接进入今天的主题
看看如何计算这个W和B
那我们先别搞那么复杂的非线性函
先从最简单的一个线性函数入手
首先第一个问题就是什么样的W和B是好的?
答案其实很简单
我们的目标是让这个函数的输出结果尽可能地接近真实数据
因此好的W和B就是能够使得函数的尽可能拟和真实数据的那一组参数
那接下来第二个问题
什么叫拟合得好
先别想那么多

image-20250521203531802

从直觉上理解这条线就拟合得挺好
左边的这个就拟合得不好
那第三个问题就自然浮出水面了
怎么用数学语言表达
刚刚我说的这个直觉上的理解呢
很简单
我们可以在每个数据点上画一条竖
使其与拟合的直线相交
由于这里的每个点的纵坐标
表示的就是真实数据
我们用Y来表示落在直线上的点
表示预测数据
我们用y hat来表示

image-20250521203624697

那么这条线段的长度
就是真实值与预测值的误差
为了评估整体的拟合效果
我们可以将所有这些线段的长度加
这样呢就得到了预测数据与真实数之间的总的差异
也就可以反映当前这个线性函数与真实数据的拟合度

image-20250521203704257

而这个表示预测数据与真实数据误
我们叫它损失函数
我们着重看一下这个公式
这个绝对值有些讨厌
数学优化时不太友好
我们做数学题时
往往最讨厌碰到这种带绝对值的问
要各种分类讨论啊
想想就头疼
所以我们改造下
用平方来代替
一来呢解决了绝对值不平滑的问题
二来呢也放大了误差较大的值的影
然后我们再根据样本的数量平均一下
消除样本数量大小的影响
那最终得到这个公式就叫做均方误差

image-20250521203752879

而均方误差就是用来表示损失函数一种
我们把损失函数记作L那从参数的视
它就是一个关于W和B的一个函数

image-20250521203819061

好先不要过于陷入这个公式的细节
那还记得我们要干啥

image-20250521203853848

损失函数表示的是真实值与预测差距
而我们的目的呢就是让这个误差最小的w和b
也就是找到可以让这个损失函数L最小
那个W和B那怎么求解呢
自然就是用我们初中就学过的
让其导数等于零求极值点的过程
我们先不上公式
通过一个具体的例子来说
假设我们就四个样本数据
就是简简单单的11223344这样
然后我们的线性模型也简单点
把B去掉
只保留一个W
其实呢就是一个简单的经过原点的一条直线Y等于WX这个时候我们展开一下损失函数
把y heat值代入进来
把求和符号展开
然后再把上面这组XY的数据带进来
平方展开再化简好
那化简之后我们就可以清晰的看到
这就是一个简简单单的关于W的一个
接下来对W求导
再让其导数等于零
就可以求出W等于一了

image-20250521204022426

代入回原直线函数
此时Y等于X就是让损失函数最小
也就是最拟合真实数据的那条直线

回过头再来看一下这个损失函数
它实际上呢是一条开口向上的抛物
刚刚其实就在寻找这个最低点
采用的办法就是让导数等于零
不过我们此时的模型是简单的线性函数
而且只保留了W而忽略了B
而如果此时把B算进来
那就需要求关于W和B两个变量的
损失函数的最小值了
这个时候损失函数的图像就是一个三维图像是一个开口向上的这个碗状形状

image-20250521204113092

我们的目标同样也是找到这个二元函数最小值
所对应的那个W和B
而多元函数求最小值的问题就不再是倒数了
而是要让每个参数的偏导数等于零
偏导数在这里就不展开了
不过可能不少人学偏导数的时候都
其实很简单
对W求偏导
就是把B当作常数和一元函数求导就
在三维图像中就是这样
相当于只看到这个切面上的二维函
同样对于B也是如此。
那么刚刚这个通过寻找一个线性函
来拟和X和Y的关系
就是机器学习中最基本的一种分析方法:线性回归
回到之前讲的神经网络
神经网络是一个通过线性函数和非线性激活函数不断组合形成的一个非常复杂的非线性函数
那么它对应的损失函数则是更复杂的非线性函数
往往呢不能像刚刚那样通过让导数等于零直接求出最小值
那怎么办呢?
人们的解决办法也非常的简单粗暴
就是一点点试
具体怎么试呢?
假如此时W和B的值均为五
损失函数计算结果是十
我们来看一下
尝试把W增加一个单位变成六
再次计算损失函数
发现结果是九
那就说明W的这次调整是对的
让损失函数变小了
再尝试把B增加一变成六
发现损失函数增加了二
变成了11
说明B的增大会让误差变大
那我们就反过来把B往小的调整
让损失函数继续变小
那如此循环往复不断调整就可以了
总之我们每次都看下当前状况
调整W或B对损失函数的影响
然后每次呢把参数向着让损失函数

image-20250521204322611

那个方向调整一点点
直到让损失函数足够小,那具体怎么调?

回到最初始的状态
W变化一点点,使得损失函数会变化多少?
这其实就是损失函数对W的偏导数
对B来说同样也是如此
而我们要做的就是
让W和B不断往偏导数的反方向去变
那具体变化的快慢呢
再增加一个系数来控制
我们叫它学习率

image-20250521204416292

这些偏导数所构成的向量就叫做梯度

image-20250521204500187

而不断变化W和B让损失函数逐渐减小的一个过程,进而求出最终的W和B,这个过程就叫做梯度下降
嗯不少材料在介绍梯度下降时
可能经常会用到人们下山这个过程
但我倒是觉得直接看公式也挺直观
没必要好了
这个公式很好理解
但是关键就在于这个偏导数该怎么求?
在之前的线性回归问题中
偏导数就是一个一元二次函数
求导非常简单
但是在神经网络中
函数本身就是一个复杂到变态的非线性函数
那更别说损失函数了
直接求导可能就不太好求了
那怎么办呢
其实也很简单
虽然神经网络整体所代表的函数很复杂
但是层与层之间的关系却是非常简单
我们就用上面这个麻雀虽小
但五脏俱全的一个简单的神经网络来举例子
只有一个输入和一个输出
而且中间的隐藏层也只有一个神经元
首先我们根据输入X的值计算出隐藏层a的值
这里的G就是随便一个激活函数
比如说sigma的无所谓
然后呢再根据A的值计算出输出层y hat
然后再根据y hat的值以及真实值Y计算出损失函数
那这里损失函数就用均方误差了
而且由于只有一个输出数据
所以说我把求和符号也省去了
这个神经网络结构中一共有四个参
要通过梯度下降的方式逐渐求解
那之前也说了
关键问题就是求出L对他们的偏导数
那我们直接拿最难的w1来举例
如何求出L对W的偏导数呢
其实很简单
从偏导数要表达的意思出发一下就能想明白
其实我们就想看w1变化一点点
会使得L变化多少了
那我们就先看W1变化
一个单位会使得A变化多少
再看看a变化一个单位会使得y head
然后再看y hat的变化
一个单位会使L变化多少
每一个都是一个简单的偏导数
那把这三者乘起来
就知道W变化一个单位会使得L变化多少了

image-20250521204746002

如果实在想不明白的话
可以联想一下齿轮怎么计算

image-20250521204830024

第一个转一圈会使得最后一个齿轮
其实就是乘起来吧
那这种偏导数的计算方式就叫做链式法则
其实就是微积分中的复合函数求导
由于我们可以从右向左依次求导
然后逐步更新每一层的参数
直到把所有的神经网络的参数都更新一次
在计算前一层时用到的一些偏导数
后面也会用到
所以说不用计算那么多
而是让这些值从右向左一点点传播
我们把这样一个过程形象地称之为反向传播
那结合之前的知识
我们通过前向传播
根据输入X计算出输出Y
然后再通过反向传播计算出损失函树
关于每个参数的梯度

image-20250521204925053

然后每个参数都向着梯度的反方向
这就构成了神经网络的一次训练
而神经网络经过多轮这样的训练
里面的参数都一点一点的变化
直到让损失函数足够小

image-20250521204944396

也就是找到了我们想要的那个函数
虽然听起来很简单
但是面对真实问题时
往往却存在着各种各样的难题
具体会遇到什么样的难题
又该采取什么样的办法去解决呢
欲知后事如何
且听下回分解
回顾一下:
为了找到一组W和B来拟合真实数据
我们定义了损失函数
并且通过制定让损失函数最小化这
来计算W和B的值
接下来我们通过简单的线性回归问题
可以直接让损失函数的导数等于零
一步就求到W和B
但神经网络的复杂性
让我们没有办法直接得到W和B的解
只能通过一点点往偏导数的反方向
调整每个参数来慢慢逼近真实答案
这个方法就叫做梯度下降
而由于神经网络的层数较多
直接求偏导数非常困难
因此可以逐层求导
间接得到最终的偏导数
这就是链式法则
而通过链式法则求导并逐层更新参
这个过程就叫做反向传播
那不断的前向传播,反向传播,这就构成了神经网络的训练过程

03 调教神经网络怎么就这么难呢?

我们知道神经网络的本质就是线性变换套上一个激活函数不断组合而成的一个非常复杂的非线性函数,并且巧妙地通过梯度下降一点一点地计算出神经网络中一组合适的参数,那这样看起来其实不是神经网络只有足够大,什么问题都能解决了?

image-20250521205132364

理论上是这样。

这一幅图你觉得那边拟合的好呢?

image-20250521205418285

如果从损失值最小来看,右边的好,但是根据直觉来看右边这个好像好的有点太过了。结果可能是只适合训练数据,对于新数据的预测反而不如左边的准

image-20250521205559524

这种在训练数据上表现得很完美但是在测试数据上表现得很糟糕的现象我们叫它过拟合。而在没见过的数据上的表现能力我们叫它泛化能力。那为什么会过拟合呢?是因为训练数据本身是一个很简单的规律,但是模型太复杂了,把那些噪声和随机波动也给学会了,这该怎么办?自然就是简化一下模型的复杂度了,比如这个案例中你使用一个非常复杂的神经网络来训练效果甚至不如线性模型好,还可以增大数据量,这样模型也会变得相对简单了,但是有的时候我们确实无法手机或者说懒得收集更多的数据,怎么办?那就在原有的数据中创造更多的数据,比如在图像处理中,我们可以对图像进行旋转,翻转,裁剪,加噪声等操作

image-20250521210101561

创造出更多的新的训练样本,这就叫做数据增强。

这样不仅仅能够产生更多的数据还刚好训练了一个让模型不因为输入的一点点小的变化而对结果产生很大的波动,这就是增强了模型的鲁棒性(Robostness).

刚刚是从数据和模型的本身入手来防止过拟合。那有没有可能从训练过程入手组阻止过拟合的发生呢?

其实训练过程就是不断调整参数的过程,只要让参数不要过分的朝着过拟合的方向发展就可以了。

image-20250521210351147

有一个简单到你都不相信的方法就是提前终止训练过程,差不多就行了,但是这种方法还是太粗糙了。那有没有什么方法可以直接抑制参数的野蛮增长呢?非常简单,你想想参数是怎么被训练出来的,是不是通过让参数往损失函数变小的方向不断调整,也就是梯度下降,那我们可以在损失函数中把参数本身的值加上去,这样在参数往调大了调时,如果让损失函数减小的没有那么多,导致新的损失函数反而是变大了,那么此时的调整就是不合适的,因此一定程度上一直了参数的野蛮生长

image-20250521210757072

除了可以用参数的绝对值之和之外,我们还可以用参数的平方和,这样参数大的时候抑制能力就更强了

我们把这一项叫做惩罚项,把通过这种向损失函数中添加权重惩罚项,抑制其野蛮增长的方法叫做正则化

上面绝对值相加的叫L1正则化,下面平方和相加的叫L2正则化

然后和之前梯度下降时增加学习率控制下降力度一样我们也增加一个参数来控制惩罚项的力度,我们叫它正则化系数,而这些控制参数的参数,我们以后统称为超参数

那为什么简简单单的公式叫什么L1正则化,L2正则化呢?因为绝对值之和叫做L1范数,而平方和的平方根叫做L2范数,这是向量空间中范数的概念

image-20250521211051814

总之这些参数都只是为了抑制参数的野蛮增长罢了

除了这种方式以外还有一种简单到发指但是就是效果显著的方法

想想看,我们的目的时为了防止让模型过于依赖某几个参数

举个形象的例子,加入神经网络的参数是一支军队,里面有好多普通士兵,但是其中混入了一支战斗力极强的战士

如果每次训练都有战士主导战局,那么你会误认为这整体战斗力很强,一旦遇到特殊情况那就会败北,这就是过度依赖少量参数的典型表现,那怎么办呢?我们可以在训练的过程中每次都随机丢弃一部分参数让战士偶尔缺席,这样模型就必须习惯去依赖大部分士兵从而避免了在某些关键参数上过度依赖的风险,虽然听起来玄学但是确实十分有效,这种方法叫dropout,这个方法是大名鼎鼎的神经学之父辛顿提出的。

除此之外模型还会遇到其他问题比如梯度小时,也就是网络越深,梯度反向传播时会越来越小导致参数更新困难,梯度爆炸,梯度数值越来越大,参数的调整幅度失去了控制。收敛速度过慢,可能陷入局部最优或者来回震荡,计算开销过大,数据规模量太庞大了,每次完整的前向传播和反向传播都非常耗时。每个问题,人们都想了各种办法来解决。比如用梯度裁剪来解决梯度的更新过大,用合理的网络结构比如残差网络来防止深层网络的梯度衰减,用合理的权重初始化和将输入数据归一化让梯度分布更平滑,用动量法,RMSprompt,Adam等自适应优化器来加速收敛,减少震荡,用mini-batch把巨量的训练数据分割成几个小批次来降低单次的计算开销,这里的每个概念展开都是一个全新的世界

image-20250521221946107

但是它们都是为了让训练过程更好罢了

image-20250521223926584

04 神经网络中永远也搞不明白的矩阵和CNN

我们直接进入主题
一个最简单的神经网络,就是Y等于WX加B套上一个激活函数
那如果输入变成了两个,那么就是两个W和两个X
如果输入变成了三个,那么就是三个W和三个X
以此类推

image-20250525231901089

我就不写了
那如果输出变成两个
再来一行公式就可以了
那这里的W的标号保证不一样
能区分开就行

image-20250525231930237

比如说这个W12
就表示第一个神经元的第二个参数
好你发现一个问题没有
就是这样写下去的话
太麻烦了
要是神经元多了的话,公式密密麻麻的,没有数学的简洁之美
那这怎么办呢?
别急
现在我们的注意力放在这个公式上
注意看啊,我要变形了
image-20250525232007741

其实就是把加减乘除替换成了矩阵运算的写法
这里先忽略一下激活函数哈
重点看中间这个矩阵的乘法
矩阵乘法很简单
我们错个位
就是这一行W的元素
分别和X这一列的元素相乘
并求和
得到的结果呢放到这里
那同样对于第二行也是如此

回到刚刚
我们现在把这些矩阵都替换成新的字母
这里我们用大写的Y表示
这里用大写的W表示
这里用大写的X表示
这里用小写的B来表示

image-20250525232057332

那么整个公式就化简成了这个样子

image-20250525232108728

不过现在还有个问题
就是神经元的层并没有体现在公式中
那假如神经元再多几层怎么办呢?
那我们此时抽象一下
也别分什么XY和隐藏层了
就通通用字母a来表示
那输入层就当做第零层
用A中括号零来表示
以此类推

那么第一层的公式就是这样
第二层的公式就是这样
第三层的公式就是这样

image-20250525232138692

我们用L表示在第几层
那么最终的通用公式就是这个样子

image-20250525232157117

每一层的神经元的值都是上一层的函数
那我们费了这么大劲
简化这个公式有啥用呢
一方面是公式简单了
也更抽象了,有利于我们进一步讨论更深的问题,另一方面是麻烦的加减乘除替换成了矩阵运算
可以充分利用GPU的并行计算的特性,加速神经网络的训练和推理过程,这就不仅仅是秀写法上的一个操作了
那回到这个公式和神经网络结构,可以看到这里的每个神经元
都与前一层的所有神经元相连,当然我们一直认为这是理所应当的
但它其实只是神经网络结构中的一种叫做全连接层

image-20250525232307356

也就是说还有其他不是全连接的结构吗?
别急
我们先来看一下全连接层的问题
假如我们现在要做个图像识别的模型
假如输入是个30×30的灰度图像
那么平铺展开后
喂给输入层的就是900个神经元
假如下一层的神经元的数量是1000个
那么这个全连接层的总参数量就达到了90万

image-20250525232333495

这太大了
另外呢这里仅仅是把输入的图片平铺展开
无法保留像素之间的空间关系
图片稍稍动一下
可能所有神经元都和原来完全不同
但从图片整体上看
可能仅仅是平移或者变暗
这就是不能很好地理解图像的局部模式
那怎么办呢?
我们随便在这个图像中取一个3×3的矩阵
这里面的数值就是颜色的灰度值
然后我们再来一个固定的矩阵
比如这样把这两个矩阵进行这样的一个运算
46×0加上75×-1
加上82×0
也就是把每个对应位置处的值相乘并求和

image-20250525232424806

最终得到一个值是250
然后我们再选取一个地方再次进行这样的运算
最终我们把这种运算方式遍历划过原图像的每个地方
得出的数值形成一个新的图像

image-20250525232443112

那这种方式叫做卷积运算

image-20250525232513024

而刚刚我们这个固定的矩阵叫做卷积核
卷积核不是一个新的概念
在传统的图像处理领域
卷积核是已知的
可以达到一定的图像处理效果
比如模糊效果
浮雕效果
轮廓效果以及刚刚的锐化效果等等
就是PS的常规操作嘛
那在深度学习领域
卷积核的值就是未知的
和神经网络中的其他参数一样
是被训练出来的一组值
那回到刚刚的经典神经网络结构
其实就是把其中一个全连接层替换成了卷积层
这就大大的减少了权重参数的数量
同时还能更有效地捕捉到
图片中的一些局部特征
可谓是一举两得
而从公式上看

image-20250525232602503

其实就是把原来的矩阵的标准乘法及差乘替换成了卷积运算
那接下来我们的神经网络
就不用再画成一个一个的小圈了
而用更抽象更简洁的图来表示
像这样在图像识别的神经网络结构中
除了卷积层外
通常还有池化层作用
是对卷积层后的特征图像进行降维减少计算量同时呢保留主要特征
这里的卷积层,池化层,全连接层都可以有多个
而这种适用于图像识别领域的神经网络结构

image-20250525232649478

就叫做卷积神经网络CNN
之前我们展示的手写数字识别的CNN可视化
就是这样的网络结构,最开始是一个输入层
我们写了一个数字六,然后是卷积层,池化层,再卷积层,再池化层
然后第一个全连接层
第二个全连接层
最终输出识别出是六

image-20250525232726717

而使用卷积神经网络非常方便可视化
我们可以看到训练过程中所训练出的卷积核
从原始图像中提取了什么样的特征
虽然这些都是中间隐藏层的事情
但是却能神奇地观察出一些实际意义
这也是卷积神经网络让人着迷的地方
好我们来回顾一下今天讲的内容
非常简单
我们把之前一个一个加减乘除很麻烦的写法
写成了矩阵的形式
一是为了方便讨论
比如刚刚介绍CNN的时候
就从公式直接看出
就是差乘变成了卷积运算而已
二是可以更好的利用GPU的并行计算提高效率
那接下来我们把之前默认的那种
所有神经元都连起来的形式叫做全连接
进而呢通过图像识别这个任务
意识到了全世界的局限性
接下来我们通过卷积运算
代替了全连接层的标准矩阵乘法
一方面使得训练参数大大的减少
另外一方面也更有利于提取图像的局部特征
这就解决了我们一开始说的问题
最后我们把神经网络结构再次抽象一个层次
原来我们画的各种小圈圈
在更高的视角下
其实就是个全连接层而已
那么这些全连接层,卷积层,池化层的组合就构成了卷积神经网络CNN
当然卷积神经网络CNN也只是神经网络结构中的一种
而且呢它有一个致命的局限性就是它主要用于静态数据比如说图片
那么如果我们要处理的是时间序列,文本,语音视频等动态数据
就需要引入另外一种神经网络结构了,它可以说是现在我们大语言模型的鼻祖了
好我们用了四个视频的内容
终于把前面所需要铺垫的知识
从头到尾给推出来了
那下个视频开始
我们就可以坐着我们这几个视频搭载的火箭
冲刺到现代AI技术的最前沿
请大家做好战斗准备

05 语言居然可以被计算出来?从 RNN 到 Transformer

给你几个字
让你生成下一个字
给你一句话
让你判断每个词的褒贬
如果把这些设计成一个神经网络的函数
来实现这个功能
你该怎么做呢?先别急
要想把这些文字作为输入参数
首先得把这些文字变成计算机能够识别的数字
这个过程就叫做编码
那具体怎么编码呢
有两种极端的方式
一种是只用一个数字标识来代表每个词
比如1代表我
2代表你3568代表地球等等
你的词表有多大
数字标识的范围就要有多大
这样的缺点非常明显
就是维度太低了
相当于一个一维的向量
而且数字标识本身对语言理解没有任何意义
无法灵活地衡量词和词之间的相关性
那另一种极端的方式是
准备一个超级超级大的向量
每个词只有向量中一个位置是1
剩下的都是零

image-20250525233554105

这种编码方式叫做one hot
翻译过来叫独热编码
one hot的缺点也非常明显
就是维度太高了
而且非常稀疏
假如此表中有10万个词
那么这就是一个10万维度的向量
而且每个向量之间都是正交的
所以词和词之间仍然无法找到相关性
那如果把向量中每个位置都看作一个特征的话
这里就相当于每个特征都是非常死板的
是或者否维度太高不好
维度太低也不好
那简单了
弄一个不高不低的就好喽
这种方式就叫做词嵌入 word embedding
通过磁嵌入的方式所得到的磁向量
维度不高也不低
每个位置数依然可以理解为某一个特征
只不过这是训练出来的
不是我们人定的

image-20250525233700713

所以特征是什么
可能我们人类完全无法理解
那为什么这种方式可以表示词和词之间
语义上的相关性呢?
我们可以用两个向量的点击或余弦相似度
来表示向量之间的相关性
进而表示两个词语之间的相关性

image-20250525233725719

这就将自然语言之间的联系
转化为了可以用数学公式计算出来的方式很关键
同时一些数学上的计算结果
也能反映出一些现实中很神奇的解释
比如一个训练好的词嵌入矩阵
可能会使得桌子减去椅子等于鼠标减去键盘

image-20250525233806254

你可以暂停下来
体会一下这里面蕴含的有趣的深意
把所有词向量组成了一个大矩阵
这个大的矩阵就叫做嵌入矩阵
这里的每一列就表示一个词向量
像刚刚说的这个矩阵
不是我们人类手动给每个词赋值而形成的
是通过深度学习的方法训练出来的
比如比较经典的方式就是word2back
这里就不展开讲解了
你就当做已经有了一个这样的嵌入矩阵
每一个可能的词语
都可以从这里找到对应的词向量
这些词向量的维度非常高
所以它所在的空间的维度也非常高
这个空间就叫做潜空间
我们人类对二维空间很好理解
最多到三维空间也还行
再往上就想象不出来了
那么这些词在高维前空间中的相对位置关系
虽然可以通过点击或余弦相似度算出来
但最好有一种直观的方式
能让我们亲眼可视化的看到
哪怕不那么准确也行
于是便有了一些方法将这个潜空间降维
投影到二维或三维的坐标系中

image-20250525233937998

来直观的可视化不同词语之间的距离
还是非常有趣的

image-20250525233957662

好了
有关词嵌入和嵌入矩阵
我们就先聊到这里
这时每个词都可以编码成向量
然后送到神经网络输入端的神经元中了

image-20250525234017983

我们再来看看最初的需求:输入一句话输出每个单词的褒贬性

image-20250525234033257

这里有12345 5个词
通过词嵌入
把每个词变成一个300维的磁向量
那么输入端就要一共有1500个神经元

image-20250525234106849

这样行不行呢
当然可以
但是有两个问题
一个是输入层太大了
而且会随着一句话中词语数量多少而变化
是变长的,不确定的
另一个是无法体现词语的先后顺序
仅仅是把它们非常生硬的平铺展开成了一个非常大的向量,一股脑地送入了输入层
这就好比我们之前说的图像识别领域
把一张图片的所有像素点展开成一个大向量
一股脑地送入输入层
一个道理
这样既增加了神经元的个数
又不能很好地抽象出特征和关联
有点费力不讨好
那在CNN中
我们是通过卷积操作提取了图像的特征
那么在自然语言处理领域
我们可以通过什么办法?
既能解决词语之间的先后顺序问题
又能降低输入层的参数量呢?
首先我们还是用经典的神经网络
但不要输入一句话
而是输入一个词
输出就是这个词是褒义还是贬义
当然这里的字母都表示矩阵就不再赘述了
这时假设第二个词来了
也是经过一样的神经网络,很简单
那此时我们用尖括号表示是第几个词
这样就有了顺序关系
那现在的问题是
第二个词的计算过程
完全没有让第一个词的任何信息参与进来
那这该怎么办呢

image-20250525234241853

答案已经写在脸上了
那就让他参与进来就好喽
那可以这样
我们让第一个词经过非线性变换后
别急着直接输出
结果先输出到一个隐藏状态H1
然后再经过一次非线性变换得到输出Y1
接下来这个隐藏状态H1的值和第二个词X2
一起参与运算
那同理对第二个词的流程也是一样
先输出一个隐藏状态H2
然后继续往下传递
那这样的话呢
前面的词的信息就这样不断的往下传递

image-20250525234322191

直到传到最后一句话的最后一个词那里
这样就把一句话中
所有的词的信息都囊括进来了
当然这里的W就要有所区分了

image-20250525234355487

有专门针对磁向量的WXH矩阵
有专门针对隐藏状态的WHH矩阵
以及最终计算输出结果的WHY矩阵
那同样对于偏执向B也是如此
把这个图简化一下
那这就是循环神经网络RNN

image-20250525234409069

当然了
还会有个图这样画
那这个RN模型就具备了理解词和词之间
先后顺序的能力
那这样就可以解决
判断一句话中各个单词的褒贬词性
给出一句话
不断生成下一个字
以及翻译等多种自然语言处理的工作了
那如果你还有些懵的话
我们再把矩阵展开来看看
首先第一个词X1和权重矩阵WXH相乘
得到第一个词的隐藏状态
H1准备往后传
H1和权重矩阵WHY相乘
得到第一个词的输出结果Y1这时候计算第二个词
同样要和权重矩阵WXH相乘
但注意这个时候要把第一个词的隐藏状态加到输入向量里拼接起来
同时权重矩阵也增加一个WHH最终计算出第二个词的隐藏状态H2准备继续往后传
那后面的流程就一样了

image-20250525234544512

最后看一下公式
其实非常简单
和经典的神经网络相比

image-20250525234558252

就是多了一个前一时刻的隐藏状态而已
回顾一下
其实本期的内容非常简单
我们想处理自然语言的一系列问题
首先就要把词转换成计算机能够识别的数字
这个过程叫编码
通过编码词而形成的向量叫做词向量
编码词向量有多种方式
其中一种是准备一个词表大小的向量
只有一个位置是一
这种方式叫做one hot及独热编码
这种编码方式维度太高
词之间缺乏相关性
所以另一种更有效的方式叫做词嵌入
词嵌入所需要经过训练而得到的矩阵
叫做嵌入矩阵
磁向量之间的相关性
可以用点击或余弦相似度来计算
有了磁向量之后
就可以输入到神经网络进行各种训练了
经典的神经网络无法表达词的先后顺序
因此我们增加了一个隐藏状态
在词和词之间传递,不同的词
使用不同的时间步T来表示
那这个不同于经典神经网络的结构
就叫做循环神经网络RNN
当然RNN还有两个非常严重的问题
1.信息会随着时间步的增多而逐渐丢失
无法捕捉长期依赖
而有的语句恰恰是距离很远的地方
起到了关键性的作用
2.RNN必须按顺序处理
每个时间步依赖上一个时间步的隐藏状态
的计算结果
那为了解决这些问题
人们使用GRU和LSTM改进了传统的RNN

image-20250525234737971

但是这些仍然是建立在让信息一点一点
按照时间簿传递的思路来解决
只能缓解而无法根治
那我们是否有一种可以彻底抛弃这种顺序计算
直接一眼把全部信息尽收眼底的新方案呢
有的那就是transformer

06 Transformer 其实是个简单到令人困惑的模型

用神经网络做个翻译任务
I love you Baby
我爱你宝贝儿
先用词嵌入的方式把每个词转换成一个词向量
简单点
假设维度就是六
如果把每个词直接丢到一个全连接神经网络中
那每个词都没有上下文的信息且长度只能一一对应
不太行
如果用循环神经网络RNN又面临串行计算
而且如果句子太长
也会导致长期依赖困难的问题也不太行
那这也不行
那也不行
可咋整呢?
小孩子才做选择
成年人全都不要直接发明一个全新的方案
跟我走
首先我们给每个词一个位置编码
表示这个词出现在整个句子中的位置
具体怎么计算
再说

把位置编码加到原来的词向量里
现在这个词就有了位置信息
但此时每个词还没有其他词的上下文信息
也就是注意不到其他词的存在
那怎么办呢
接着看
别眨眼

QKV分别是查询(query)、键(key)、值(value),刚刚问大模型是这么回答的,不清楚有没有错误,明白原理的大佬帮忙指正: 1. Query:当前需要处理的词(比如翻译到中文的「苹果」)。 2. Key:句子中每个词的「身份标识」(比如英文单词 “apple”、“red”、“eat” 的含义特征)。 3. Value:这些词实际携带的语义信息(比如 “apple” 对应水果、公司等含义)。

首先我们用一个WQ矩阵和第一个词向量相乘
得到维度不变的Q1向量
这里的WQ矩阵是可以通过训练过程学习的一组权重值
同理我们用wk矩阵和第一个词向量相乘得到K1
再用WV矩阵得到V1
接着对其他词向量也和相同的WQKV矩阵相乘
分别得到自己对应的QKV1向量

image-20250525235101479

image-20250525235117721

当然实际在计算机GPU中运算的时候
是通过拼接而成的大矩阵做乘法
并不是像我们刚刚那样一步一步计算的
得到的直接就是包含所有词向量的QKV矩阵

不过为了理解
我们解释的时候还是拆成一个个的词向量
现在原来的词向量已经分别通过线性变换
映射成了QKV

维度和原来是一样的
接下来我们让Q1和K2做点击
这表示在第一个词的视角里
第一个词和第二个词的相似度是多少
同理依次和K3做点击表示和第三个词的相似度
和K4做点击表示和第四个词的相似度
最后呢也补上一个和自己做点击表示和自己的相似度
那拿到这些相似度的系数后

image-20250525235224109

分别和V向量相乘

image-20250525235241089

再相加得到A1
那此时这个A1就表示在第一个词的视角下
按照和它相似度大小按权重把每个词的词向量都加到了一块儿
那这就把全部上下文的信息都包含在第一个词当中了而且是用第一个词的视角来看的
同理其他几个词也按照这种方式,那么此时每个词都把其他词的词向量按照和自己的相似度权重加到了自己的词向量中


那这里的什么QKV都是中间的计算过程了
我们从全局视角看
现在就是把最初的输入的词向量经过一番处理
变成了一组新的词向量
不一样的是呢
这组新的词向量中
每一个都是包含了位置信息和其他词
上下文信息的一组新的词向量

image-20250525235334042

这就是注意力机制attention做的事情
我们再进一步优化下
有的时候一个词和另一个词的关系
可能从不同的视角看是不一样的
对于注意力机制来说,如果只通过一种方式计算一次相关性
灵活性就会大大降低
所以我们做些改进
之前我们是每个词计算一组QKV
现在我们在这个QKV基础上
再经过两个权重矩阵变成两组QKV
给每个词两个学习机会

image-20250525235414626

学习到不同的要计算相似度QQV
来增加语言的灵活性
这里的每组QKV成为一个头 head
接下来在每个头里面的QQV
仍然经过刚刚的注意力层的运算
得到A向量
然后把两个A向量拼接起来
得到了和刚刚一样的结构
而对于刚刚的注意力机制attention
这种方式就叫做多头注意力Multi head attention
而我们刚刚举的例子就是两个头的情况


那我现在要恭喜你
已经把transformer架构
最核心的逻辑都搞清楚了

image-20250525235456217

你信不信不信的话
我们对照一下transformer的经典论文
中的架构图来看看
首先第一步就是把输入的内容
通过词嵌入的方式转换成词向量矩阵
对应的就是这里
第二步加入位置信息
其实就是再加个形状一样的矩阵
对应的就是这里
第三步经过多头注意力的处理
输出的矩阵维度和输入没有变化
给每个词向量增加了上下文信息
对应的就是这里
后面还有一步添加了残差网络和归一化处理是为了解决梯度消失并且让分布更加稳定而做的优化
我们刚刚没有展开这块儿
那对应的就是这里
同时我们也可以看到
整个transformer的标准架构中
最主要的就是多头注意力的处理
相当于我们把这些部分的逻辑都搞明白了
快给自己鼓鼓掌吧
下面深入到多头注意力机制的细节部分
我们再看看

image-20250525235609581

如果是不分多头的单头注意力
那么就是先让Q和K相乘
得到一个相似度系数的一个矩阵
然后再和V相乘
最终得到了包含上下文信息的磁向量矩阵
省略了中间的缩放掩码和一层soft max处理
再看右边的多头注意力情况

首先QKV分别经过线性变换
拆分成多组
相当于给了多次机会学习到不同的相似度关系
依次经过注意力机制运算后
把运算结果拼接起来
是不是完全一样呢
不过我们讲解的时候还省略了一次线性变换
即多头结果并不是简单的拼接起来
还需要再次经过一层权重矩阵的乘法
这时候再看两个核心公式就很好理解了
所谓注意力运算就是QK矩阵相乘经过缩放
在经过soft max层处理

image-20250525235647356

最后和V相乘
对于多头情况
就是先将QKV矩阵
经过多个权重矩阵拆分到多个头中
分别经过注意力机制的运算
最后合并起来
再经过一次矩阵运算
得到了输出

再回过头来看下这个全局的图
左边的部分叫做编码器
右边的部分叫做解码器

你实在不愿意叫也没关系
假设这个是用于翻译的任务
我们训练这个神经网络的过程是
首先输入要翻译的文本
I love you baby
然后经过词嵌入引入位置编码
经过多头注意力残差和归一化处理
接着送入一个全连接神经网络
再残差和归一化处理
结果送入解码器的一个多头注意力机制的两个输入中作为KV矩阵
再看右边解码器的部分输出是翻译后的文本
我爱你宝贝儿
同样经过此嵌入
引入位置编码
经过多头注意力
然后残差和归一化处理下
然后送入上面说的多头注意力的一个输入中
作为Q矩阵和刚刚从编码器中送入的KV矩阵
再经过多头注意力残差归一化
再全连接神经网络
再残差归一化
最后再经过一层线性变换的神经网络
投射到此表向量中
最后用soft max层转化为概率
这就代表预测的下一个词在词表中的概率分布
那我们取概率最高的就是下一个词应该是什么
这里有一个小的不同
就是有个掩码

image-20250525235842641

这个掩码的作用是真正推理
翻译时是一个词一个词翻译的
比如说这个时候翻译到我下一个词应该是爱
所以输出我的时候是看不到后面的词的
这就需要掩码来把后面的词遮挡住
以便训练的时候模拟真实推理场景时的过程
比如当此时输入是i love you baby
输出只有一个词
我的时候经过这个神经网络
最后上方输出词表的概率分布
我们想要的结果就是I字的概率值最大
如果训练时有偏差
那么就计算损失函数
再反向传播
调整transformer结构中的各种权重矩阵
直到学习好为止
总的来说
transformer确实是个特别简单的架构
原文中也是这样说的
尤其是当你有了基础的神经网络知识之后
如果你看了这个系列之前的视频
那就只有多头注意力
这一层是陌生的
但是它其实拆解之后也是各种矩阵相乘呀
相加呀
这种操作罢了
那其余的词嵌入位置
编码残差归一化
经典神经网络
soft max层等等
都是我们之前的视频中已经了解过的概念
把这些老东西拼凑拼凑就诞生了
我们现在大模型技术的鼻祖transformer
那GBT的底层其实就是transform的一半
即只有解码器的部分
也不翻译
谁只管看前面的词
猜下一个词
别看他来回的猜词猜词
猜着猜着就变成了聊天写代码
解数学题的全能选手了
那transformer的架构来源于经典的论文
Attention is all you need
本期视频的内容理解之后再去看这篇论文
你就会发现非常非常非常容易理解
因为它本身就是一个很简单的架构
也正因为简单粗暴
但是效果却出奇的好
所以才会广为流传
并成为现代大模型的基础

本文内容源自B站系列视频 https://www.bilibili.com/video/BV1wXQhY8EJ6?vd_source=b0f2742e7b4a2838ad4d1870af693bc1