第一次接触机器学习的时候,是我们导师让我们去把CNN这个模型跑下来。当时就先copy了一个GitHub上面的项目,跑成功之后,里面有很多概念,模型、参数、损失函数、梯度下降、反向传播、神经网络、Transformer……每个词单独拿出来都能讲半天,一堆概念叠在一起之后,但还是不明白:AI 到底是在干什么?

但其实整套东西一直围绕着同一个问题展开:我手里有一堆过去的数据,能不能让计算机自己从这些数据里找到某种规律,然后用这个规律去判断它以前没有见过的新东西?

01|为什么有了编程,我们还需要机器学习?

假设今天让我写一个非常简单的电商推荐程序,我想判断一个用户喜欢什么。按照传统编程的思路,我完全可以写很多规则:如果这个人经常看衣服,就给他推荐衣服;如果他最近搜索电脑,就给他推荐电脑;如果他经常买零食,就多给他展示食品。

问题在于,真实的人根本没有这么简单。一个人最近可能看了三双鞋、两个键盘、一台显示器,又收藏了一件夹克,晚上经常刷数码产品,但最后真正下单的却总是生活用品。这个时候,你准备怎么继续写 if-else?

当然可以继续加规则,但很快你就会发现,真实世界的情况实在太多了。100 条规则不够就写 1000 条,1000 条不够写 10 万条,到最后规则之间甚至会互相冲突。传统程序的问题并不是不能解决复杂问题,而是有一些规律复杂到很难由人提前完整地写出来。

再比如识别猫和狗。人看到一张照片,可能一眼就知道这是猫,但如果让我把什么东西一定是猫写成严格的程序规则,我可能会说猫有胡须、有尾巴、耳朵尖尖的、四条腿,可狗也有尾巴,狐狸也有尖耳朵,有些猫甚至没有尾巴,照片里还可能只露出半张脸。

这时候思路就可以反过来了。既然我自己很难把猫的规则完整写出来,那我能不能直接给计算机看几十万张猫和狗的照片,告诉它哪些是猫、哪些是狗,然后让它自己从这些数据里寻找规律?

这就是机器学习非常核心的一次思维转变。传统编程更多是人总结规则,再把规则交给机器;机器学习则是人提供数据和学习目标,让机器从数据中调整出一套规律。

所以以后再看到 Machine Learning,不要先想到神经网络、Transformer 或者各种复杂公式。你先想到一句最朴素的话就够了:让计算机从数据中学习,而不是把所有规则提前写死。

02|机器学习所谓的学习,到底在学什么?

我们再把这个问题往前推一步。机器到底学到了什么?

假设我要预测一套房子的价格。我收集了过去几年的成交记录,每套房子都有面积、房龄、卧室数量、距离地铁多远、位于什么区域,以及最后真正成交了多少钱。机器要做的事情,就是从这些过去的数据里寻找房屋信息和成交价格之间的关系。

在机器学习里面,我们经常把输入记成 X,把希望得到的结果记成 Y。于是房价问题可以写成房屋信息 X → 房价 Y,猫狗识别可以写成图片 X → 猫或狗 Y,疾病预测也可以写成检查数据 X → 某种疾病风险 Y。

这里的 X 通常叫特征 Feature,Y 在监督学习中通常叫标签 Label。比如预测房价时,面积、房龄、地段这些东西可以成为特征,而真正成交的房价就是标签;识别猫狗的时候,图片本身是输入,猫或者狗就是标签。

所以很多机器学习问题,最后都可以抽象成一个非常简单的形式:X -> Y

机器要寻找的,就是从 X 到 Y 之间的某种映射关系。这个关系可能简单到一条直线,也可能复杂到需要几十亿甚至更多参数才能表示,但最底层的问题没有发生变化。

03|为什么 AI 课程总是在讲向量、矩阵和张量?

因为计算机最终并不认识“房子”“猫”“用户兴趣”这些人类概念。对它来说,这些东西最后都得想办法变成数字,才能真正参与计算。

假设我要描述一个学生,只考虑三个特征:身高 170 cm、体重 60 kg、年龄 18 岁。那么这个学生就可以表示成一个向量:x=[170,60,18]。你可以先把向量理解成“一串有顺序的数字”,每个位置分别代表这个人的一个特征。

如果现在不是一个学生,而是 1000 个学生,每个人都有身高、体重、年龄三个特征,那么这些数据就可以排成一个 1000×3 的矩阵。你可以把它想象成一张 Excel 表格:每一行代表一个学生,每一列代表一个特征。

N 通常表示样本数量,D 表示每个样本有多少个特征,所以这个公式本质上就是在说:我们现在有 N 条数据,每条数据都有 D 个数字。

你现在甚至可以先粗暴地理解:一维的一串数字叫向量 Vector,二维的一张数字表叫矩阵 Matrix,再往更高维扩展,就经常统称为张量 Tensor。这个理解虽然比较入门,但已经足够让你看懂后面大部分 AI 课程里的数据形状。

图片也是一样。一张灰度图片在计算机看来,可以表示成一个由像素值组成的二维数组,在常见的 8 位灰度图中,每个像素通常用 0 到 255 表示亮度,0 接近黑色,255 接近白色。

比如一张 28×28 的手写数字图片,本质上就是 784 个像素值按照二维结构排列起来。人眼看到的是一个“3”或者“8”,计算机真正拿到的,其实是一大堆数字。

彩色图片会再多一层。常见的 RGB 图片中,每个像素分别记录 Red、Green、Blue 三个通道的强度,因此一张 1920×1080 的图片,可以表示成一个类似 1080×1920×3 的张量。

如果一次训练还要同时放进去 64 张图片,就会再多一个 batch 维度,变成类似 64×1080×1920×3。你现在不用死记这些形状,只要先理解一件事:图片、文字、声音最后都得先变成数字结构,模型才能处理。

这也是为什么学 AI 绕不开线性代数。现实世界里的文字、图片、声音、用户、商品,最后都会被转换成向量、矩阵或者更高维的张量,然后交给计算机进行运算。

04|为什么把东西变成向量之后,会突然变得这么有用?

真正有意思的地方就在这里。一个东西只要被表示成向量,我们就开始有机会用数学去描述以前非常模糊的关系,比如“这两个人兴趣挺像”“这两句话意思差不多”“这两张图片看起来很接近”。

假设一个推荐系统经过训练以后,把你表示成了一串数字,同时把另一个用户也表示成了另一串数字。模型在训练过程中,会慢慢学会一种“怎么摆放这些向量”的方式,让某些兴趣比较相似的人在这个数字空间里靠得更近。

如果另一个和你很接近的用户最近特别喜欢某个视频,而你还没有看过,系统就可能觉得这个视频也值得推荐给你。原来一句很模糊的“你们两个口味挺像”,现在就有机会变成“你们两个向量之间的关系很接近”。

现代 AI 里面非常重要的 Embedding,本质上就在做类似的事情:把一个复杂对象变成一串有意义的数字。这个对象可以是一个用户、一句话、一张图片,甚至一个商品。

比如一句“我今天特别开心”和一句“今天心情真的很好”,虽然里面具体的字不一样,但一个好的语言模型可能会把它们映射到比较接近的位置。因为模型学到的不是简单看两个句子有没有相同的字,而是希望把其中某些语义关系也编码进向量里面。

这里有一点特别重要:并不是随便把一个东西变成一串数字以后,距离近就一定代表相似。只有模型已经学到了一个有意义的表示方式,而且我们选择的相似度计算方法也合适,这些距离才真正有意义。

所以所谓的向量空间,你暂时可以把它想象成一个模型自己学出来的数字世界。现实中很难直接计算的“像不像”“意思接不接近”“兴趣合不合”,到了这个空间里以后,就开始有机会通过数学来处理。

05|数据有了以后,模型到底是什么?

接下来就是机器学习第二个特别核心的概念:模型 Model。这个词听起来很高级,但入门的时候完全可以先把它理解成一个里面有很多可以调整数字的函数。

最简单的模型甚至可以只有:y=wx+b。这里的 x 是输入,y 是输出,而 w 和 b 就是模型里面可以调整的参数。

假设我们拿这个模型去预测房价,x 可以代表房子的某个特征,比如面积,y 就代表最后预测出来的房价。训练的过程,本质上就是不断尝试不同的 w 和 b,找到一组更合适的数值,让模型预测出来的房价尽量接近真实成交价。

当然,现实中的房价不可能只看面积。它可能同时受到地段、房龄、楼层、交通、学区等很多因素影响,所以模型也会从一个输入慢慢扩展成很多个输入。

这时候,每一个输入特征都可能对应一个权重。模型需要慢慢学出来,面积到底重要多少,房龄重要多少,距离地铁多远又会带来多大的影响。

在最简单的线性模型里,你甚至可以把这些权重理解成每个特征应该占多少分量。但神经网络会复杂很多,它里面会有很多层,每一层内部又有大量连接,所以需要调整的参数数量很快就会从几个、几百个,变成几百万甚至几十亿个。

所以你以后看到一个模型说自己有 7B、70B 参数,不要把参数想成什么特别神秘的东西。B 是 Billion,也就是十亿,7B 大概就是 70 亿个可以通过训练不断调整的数值。

当然,这 70 亿个参数也不代表模型有 70 亿个输入特征。它们更多分布在神经网络一层又一层的权重矩阵和偏置里面,共同决定模型最后会算出什么结果。

所以模型可以先用一句话理解:它就是一套带有大量可调参数的计算规则,而训练,就是不断寻找一组更合适的参数。

06|模型一开始什么都不会,它怎么开始学习?

这是我觉得机器学习特别有意思的地方。一个神经网络刚开始训练的时候,并不是里面已经藏着什么知识,然后我们慢慢把它唤醒;它的可训练参数通常只是按照某种初始化方法得到了一组初始值。

这时候我们拿一批数据输入模型。模型根据自己当前乱七八糟的参数计算一次,然后吐出来一个预测结果,因为它现在还没学会,所以刚开始的预测通常不会很好。

假设真实房价是 300 万,模型第一次预测成了 180 万。现在我们至少知道一件事:模型错了。

但错了还不够,我们还得知道它究竟错得有多严重。于是第三个核心概念出现了:损失函数 Loss Function。

07|损失函数,其实就是给模型的错误打分

损失函数的任务很简单:衡量模型预测结果和真实答案之间到底差多少。

预测得比较接近,Loss 就比较小;预测得特别离谱,Loss 就会变得很大。于是训练模型的目标突然变得非常具体:不断调整模型参数,让 Loss 尽可能降低。

如果做的不是房价预测,而是猫狗分类,情况又会不一样。模型可能输出“这张图片有 90% 的概率是猫”,这时候我们通常会使用交叉熵之类更适合分类任务的损失函数,而不是机械地使用平方误差。

所以损失函数并不是永远只有一个公式。不同任务会设计不同的 Loss,但它们共同解决的是同一个问题:告诉模型,你现在到底错得有多严重。

08|知道自己错了以后,模型怎么知道应该往哪里改?

这时候微积分终于上场了。这个要涉及到一点数学的内容。

假设现在模型只有一个参数 w,损失函数写成 L(w)。我们真正想知道的是:如果我把 w 稍微调大一点,Loss 会变大还是变小?如果把 w 稍微调小一点,又会发生什么?

这就是导数在这里最直观的意义。你可以暂时把它理解成一个“方向提示器”,它告诉我们:当参数 w 发生一点变化的时候,Loss 大概会朝什么方向变化,而且变化得有多快。

比如导数是正的,说明在当前位置附近,w 往大的方向移动,Loss 倾向于继续变大;如果导数是负的,说明 w 往大的方向移动,Loss 反而可能变小。我们就可以根据这种变化趋势,判断接下来参数应该往哪里调整。

如果整个模型只有一个参数,一个导数就够了。但真实神经网络里面可能有几百万、几十亿甚至更多参数,每一个参数都对应一个“如果我稍微改一点,会对 Loss 产生什么影响”的问题。

于是我们把所有参数对应的这些偏导数放在一起,就得到了一个梯度 Gradient。你可以把它理解成:模型当前所有参数的方向提示打包在一起。

梯度在数学上指向函数局部增长最快的方向。说得更人话一点,就是如果现在想让 Loss 涨得最快,梯度会告诉你应该往哪边走。

但我们的目标当然不是让 Loss 越来越大,而是让它越来越小。所以训练的时候,我们通常会往梯度的反方向调整参数,这就是梯度下降背后最核心的想法。

于是就有了机器学习里非常经典的更新公式。这里的梯度 ∇L 告诉我们参数应该往哪个方向调整,η 是学习率 Learning Rate,它决定我们这一次到底迈多大一步。

09|梯度下降,可以想象成一个蒙着眼睛下山的人

想象你半夜被扔到一座山上,周围完全没有灯,你不知道整座山长什么样,也不知道真正最低的谷底在哪里。你唯一能做的,就是用脚感受当前位置附近哪边坡度最向下,然后朝那个方向走一步。

走完以后再摸一下地形,再判断下一步往哪里走。如此不断重复,这就是理解梯度下降特别直观的一种方式。

学习率控制的就是你每一步迈多大。如果步子太大,你可能一下跨过谷底,甚至在山谷两边来回跳;如果步子特别小,虽然很稳,但可能走非常久才能到达比较低的位置。

现实训练中的学习率也不是完全拍脑袋瞎猜。经验和实验确实非常重要,但现在已经有 warmup、learning-rate scheduler、cosine decay、超参数搜索等大量方法帮助我们调整学习率。

10|一直往下走,就一定能找到世界上最低的地方吗?

不一定,因为你其实看不到整座山。

还是刚才那个蒙着眼睛下山的例子。假设你一路顺着坡往下走,最后走进了一个山谷,站在这里以后发现,不管往哪个方向走,地势好像都会变高。

这个时候你很容易觉得,自己已经走到最低点了。可如果突然有人把你拉到空中,你可能会发现,翻过隔壁那座山以后,还有一个比这里深得多的山谷。

你现在站的地方,只是在自己附近已经没有更低的位置了,这种情况可以理解成局部最优 Local Optimum。如果真的是整张地图里面最低的那个位置,才叫全局最优 Global Optimum。

如果模型只有一个参数 w,我们还可以把 Loss 画成一条曲线;如果只有两个参数,也许还能想象成一张有高有低的地形图。但真实的神经网络可能有几百万、几十亿个参数,相当于同时存在几百万、几十亿个可以变化的方向。

这种情况,人脑已经没办法真正把它画出来了。数学上我们会把它描述成一个非常高维的空间,而我们平时看到的山谷图,只是为了帮助理解做出来的简化版本。

这种高维的 Loss 地形里面,还会出现一些更奇怪的位置。比如有些地方往一个方向走是下坡,换另一个方向却是上坡,这种位置叫做鞍点 Saddle Point;还有些地方特别平,梯度非常小,模型走得很慢,这类区域可以理解成平台区域。

所以训练神经网络,并不等于顺着坡一直走,最后一定能够找到整个世界最低的那个点。真实训练里面,我们很多时候甚至没有必要执着于找到一个数学意义上绝对最低、唯一正确的答案。

因为对于一个模型来说,最重要的其实不是训练集上的 Loss 到底能不能压到最低。真正重要的是:这组参数拿到以前没有见过的数据上以后,还能不能表现得好。

比如一个猫狗分类模型,把训练集里的 10 万张图片全部记得滚瓜烂熟,准确率接近 100%。但你随便从网上找一张它没见过的猫,它马上认错了,那这个模型其实没有真正学到有用的规律。

机器学习真正想要的是一种叫做泛化能力 Generalization的东西。简单来说,就是模型不只会做训练时见过的题,还能够把学到的规律用到以前没有见过的新数据上。

11|为什么训练的时候不把所有数据一次性塞进去?

假设我们现在有 100 万条训练数据,最直接的办法当然是把这 100 万条数据全部扔进模型。等所有数据都计算完以后,再把它们的梯度汇总起来,最后更新一次模型参数,这种方式可以理解成 Batch Gradient Descent。

它的问题也很明显:太贵了。

如果模型每更新一次参数,都要重新处理完整的 100 万条数据,那一次更新可能就要消耗非常多的计算资源和显存。数据量再大一点,模型再大一点,这种做法就会变得非常慢。

那我们能不能走到另一个极端,每次只拿 1 条数据?

当然可以。比如拿一张猫图,算一次 Loss,马上更新参数;然后再拿下一张狗图,再更新一次,这种方式就是 Stochastic Gradient Descent,也就是 SGD。

这样做每一步都很便宜,但问题是,一条数据的偶然性太大了。比如你刚好抽到一只长得特别奇怪的猫,这一张图片算出来的梯度,可能并不能代表整个数据集真正想让模型调整的方向,所以训练过程容易比较抖。

于是实际深度学习里,大家更多会使用一种折中的办法:Mini-batch。

比如一次拿 32、64、128 条数据,让这一小批数据一起经过模型,算出这一批数据对应的 Loss 和梯度,然后再更新一次参数。这样既不用每次把完整数据集全部跑一遍,又不会因为只看一条数据导致梯度过于随机。

所以你以后经常看到 batch size = 32、64、128,本质上说的就是:模型每次先看这么多条数据,再更新一次参数。

这些数字也不是随便规定出来的。Batch size 会受到 GPU 显存、矩阵计算效率、模型大小、数据特点等很多因素影响,有时候大 batch 更适合,有时候小 batch 反而效果更好,它本身也是训练里非常重要的一个超参数。

12|Adam 到底比普通梯度下降聪明在哪里?

现在我们已经知道,梯度告诉模型参数应该往哪个方向调整,学习率决定每一步大概要走多远。但真实训练还有一个问题:不同参数对应的路况,可能完全不一样。

有些方向特别陡,你稍微迈大一点,就可能直接冲过头;有些方向又特别平,如果每次还是迈同样的小步子,可能走半天都没什么变化。还有一些方向甚至会一会儿往左、一会儿往右,参数一直来回抖。

如果所有参数永远都按照完全一样的方式更新,训练效率往往不会特别好。所以后来就出现了很多更聪明的优化方法,Adam 就是其中非常经典的一种。

Adam 不会只看这一刻的梯度是多少,它还会参考过去一段时间梯度是怎么变化的。你可以把它想象成一个下山的人,他不只低头看自己脚下这一块坡有多陡,还会记得自己刚刚几步一直在往哪里走。

如果某个方向连续很多次都差不多,它就会觉得这个方向比较稳定;如果某个方向一直忽左忽右、抖得非常厉害,它在更新的时候就会更加谨慎。与此同时,它还会根据不同参数过去梯度的大小,对不同方向的步长做出调整。

这也是 Adam 这个名字背后的意思:Adaptive Moment Estimation。你现在完全不需要去背里面一阶矩、二阶矩的公式,只需要先理解,它比最基础的梯度下降多利用了一些历史信息,因此更新参数时可以更加灵活。

今天深度学习里还非常常见一个名字叫 AdamW。你可以暂时把它理解成 Adam 的一个常用改进版本,它在 Weight Decay,也就是权重衰减的处理方式上做了调整,在 Transformer 和很多大模型训练里面都很常见。

至于 Weight Decay 具体是怎么回事,这一篇暂时不用继续往下挖。你现在只需要先把整个位置关系搞清楚:梯度告诉我们参数应该往哪里改,而 Adam、AdamW 这些优化器,负责决定这些梯度到底应该怎样被利用。

13|反向传播到底在反什么?

到这里,我们终于碰到了神经网络训练里面最核心的东西之一:Backpropagation,反向传播。这个名字第一次看很容易让人感觉很复杂,但它实际上是在解决一个非常具体的问题。

假设一张猫的图片经过一个神经网络,前面连续经过很多层计算,最后模型输出:“我觉得这张图片有 90% 的概率是一只狗。”但真实答案告诉它,这其实是一只猫,于是损失函数算出来,这一次预测错得还挺严重。

问题是,我们现在虽然知道了整个模型错了,但模型里面可能有几百万、几十亿个参数。那到底应该改谁?第一层里面的某个参数应该改多少,中间某一层又应该改多少,最后一层是不是应该承担更多影响?

我们真正需要知道的是:每一个参数如果稍微发生一点变化,最后的 Loss 会跟着变化多少。只有把这个问题算出来,模型才知道下一步应该怎样修改这些参数。

一开始,数据从输入端进入神经网络,然后一层一层往后计算,最后得到预测结果。这个从输入一路算到输出的过程,就叫做前向传播 Forward Pass。

预测结果出来以后,我们再拿它和真实答案进行比较,通过损失函数得到一个 Loss。到这里,前向传播的任务基本上就完成了。

接下来就要反过来了。

我们从最后的 Loss 开始,先计算最后一层里面每个参数发生一点变化,会对 Loss 造成多大的影响。然后再继续往前一层算,再往前一层算,一直把这种影响关系追到网络最前面的参数。

这里真正支撑整个过程的数学,就是微积分里面的链式法则 Chain Rule。

链式法则听起来很抽象,但可以先看一个特别简单的关系。假设参数 A 会影响中间结果 B,而 B 最后又会影响 Loss,那我们现在想知道A 改一点,Loss 会变多少,就可以先看 A 对 B 的影响,再看 B 对 Loss 的影响,然后顺着这条关系把它们串起来。

神经网络虽然有很多层,但本质上也是一条非常长的计算链。前一层影响后一层,后一层继续影响下一层,所以我们最后就可以从 Loss 出发,顺着这条计算链一路往回追。

这就是反向传播这个反真正指的东西:前向传播从输入一路算到 Loss,反向传播再从 Loss 开始,一层一层往回计算每一个参数对应的梯度。

这里还有一个特别容易混淆的地方。反向传播负责计算梯度,优化器负责利用这些梯度去更新参数。

你可以把它们理解成两个不同的角色:反向传播负责算清楚“谁应该往哪边改、影响有多大”,Adam、AdamW 这些优化器才真正拿着这些结果,决定这一次参数具体怎么更新。

14|现在,我们终于可以完整看一次模型到底是怎么训练的

前面的 Data、Model、Loss、Gradient、Backpropagation、Optimizer,其实到这里终于可以全部串成一条线了。

假设我们现在要训练一个猫狗分类器。首先,我们从训练数据里面取一小批图片,比如 32 张,然后把它们一起送进神经网络,这 32 张图片就组成了一个 Mini-batch。

模型根据自己当前的参数,对这些图片进行一层一层的计算,这一步就是前向传播。比如其中有一张图片,模型最后输出:“80% 的概率是猫,20% 的概率是狗。”

结果真实标签告诉它,这其实是一只狗。

于是损失函数开始工作,把模型的预测结果和真实答案放在一起比较,计算出这一次到底错得有多严重。模型猜得越离谱,Loss 通常就越大;猜得越接近,Loss 就越小。

得到 Loss 以后,就进入反向传播。

反向传播利用链式法则,从最后的 Loss 开始,一层一层往前计算梯度。最后我们会知道,网络里面每一个参数如果稍微变一点,会让 Loss 朝什么方向发生变化。

梯度算出来以后,接下来就是优化器的工作。SGD、Adam、AdamW 这些优化器会利用刚才得到的梯度,再结合学习率等信息,对模型参数进行一次真正的更新。

到这里,一轮参数更新就完成了。

然后再从数据集里面拿下一批图片,重新前向传播,重新计算 Loss,重新反向传播,再重新更新参数。整个过程可能重复几十万次、几百万次,甚至更多。

所以你以后再看到别人说训练一个神经网络,脑子里其实可以直接出现这样一条流程:取一批数据 → 前向传播得到预测 → 计算 Loss → 反向传播计算梯度 → 优化器更新参数 → 下一批数据继续重复。

所谓模型在学习,本质上并不是里面突然出现了一个会思考的小人。真正发生的事情,是模型内部大量参数在一次又一次预测、犯错、收到反馈和重新调整之后,慢慢变成了一组能够更好完成任务的数值。

你也可以把它粗略想象成我们自己刷题。

第一次做错了,看一下自己错在哪里,然后调整做法,再去做下一道题。只不过神经网络的“错题分析”不是靠语言反思,而是 Loss、梯度、反向传播和优化器一起完成的。

到这里,机器学习最核心的一套训练循环,其实已经完整走完一遍了。

数据进来,模型进行计算;预测错了,用 Loss 衡量错得有多严重;反向传播再计算每一个参数的梯度;最后优化器真正修改参数。下一批数据进来以后,这套过程再重新跑一遍。

但这里其实还留下了一个更有意思的问题。

我们前面讲了这么久,神经网络底层做的事情,看起来无非还是数字、向量、矩阵乘法、加法,再加上一些函数。那为什么这些看起来很普通的数学运算,堆很多层以后,居然真的能够认出一只猫,甚至还能处理语言、图片、代码这些复杂得多的东西?