跳转至

第 1 天 为什么要从数学开始?

说到大模型中的数学,相信大家早有耳闻。不知道大家是不是和我一样,既充满期待又心怀忐忑地准备开始面对微分、积分、导数、微分方程、偏微分这些概念了。准确地说,是复习才对。因为我们上学的时候,在考试头一天深夜我们已经就着宿舍走廊里的灯复习过一次了,对吧?

但现在我们并不需要马上复习这些数学概念。了解这些数学工具当然是必要的,但目前我们要做一件比立即阅读这些工具的说明书更具意义的事 —— 我们得知道大模型为什么需要用到这些数学工具,大模型领域的大牛们拿起这些工具到底是图啥呢?

1.1 大模型:「最优化问题」的解

所谓最优化,就是在一堆茫茫多的选项中找到那个最好的。

一个典型的最优化问题是在科技馆中常常出场的速降曲线问题: 一个小球从高点滚到低点,走什么路径时间最短?当然,欧拉和牛顿已经告诉我们这个问题可以通过微积分求得精确解。但不幸的是,现实中我们面对的问题常常比速降曲线问题更加麻烦。并且我们也没有欧拉和牛顿那样超越时代的怪力,甚至我们都难以构建起描述问题的方程。常常,我们面对的很多最优化问题是无法求得解析解的。

ch1-curve.jpg

图 1-1 速降曲线装置

比如说,想象这样一个大家都经历过的场景——你想要讨好一个女孩。假设你要追求一个女孩,那么,什么样的女孩是可以被讨好的?或者说,什么样的女孩是非常难被讨好的?

情况一:你是她妈

你知道她是谁,你完全了解她,她想要什么。那这种情况下,你当然轻松讨好她,你都可以直接给出精确的答案,如果用最优化的语言表达,这就是存在解析解的情况。

情况二:《像雾像雨又像风》

90 年代的街头有首流行歌曲叫做《像雾像雨又像风》,说的是一个女孩在同一时刻能存在三种心情,她自己都闹不明白自己是咋回事,客观上就没有规律可循。数学里,这叫 多值函数 ,而多值函数就不是函数!它意味着输入和输出之间压根就不存在规律,那么利用数学工具完成最优化工作的想法自然也就只能是妄念。在最优化领域,这类问题我们通常推荐放弃。

情况三:普通女孩

你虽然不完全了解她,但她的情绪让你有迹可循。要生气时,她撇撇小嘴;预备开心时,她也会嘴角上扬。这种女孩,如果用数学的的语言表达,我们说她是 光滑 的。

所谓 光滑 ,意思就是在函数曲线上不存在那种陡然而变的 尖刺 ,她给你的趋势信号和情绪之间的是存在某种规律或者说模式的,并且这种模式是可被探究可被描述的。如果你认真思考,发现了这个模式,又拿个小本本把这个模式给记下来。那么,你顺利完成了这个场景的最优化任务。

你这个本本中所记录的 模式 ,其实就是 deepseek 开源的那个几百吉的大模型里存储的内容。说不上哪个更复杂,只不过,deepseek 存储的是关于人类语言的模式。

1.2 数学工具:先贤们的馈赠

饿了就吃,撑了就拉,按照模式行事本就是我的本能之一。我讨厌高数,这玩意儿我挂科了 3 次。而且没它这 40 多年我也过来了,好像也没觉出啥问题啊。

但是,兄弟们,这次可能真不行。喜欢就去追,被讨厌了放弃,在有模式的时候,循模式而动当然是每个人的本能,无须去招惹数学。可是这次的问题在于,有一个大问题,它背后其实是有模式的,是咱们不知道模式的存在。咱还以为这事是无法捉摸的小公主呢,直到 OpenAI 揭开真相。

想当年,莱布尼茨代表的大陆理性主义者相信这个世界的一切都能被数学所描述,结果被牛顿代表的经验主义整得郁郁而终。谁能知道 300 多年后的今天,真有一家公司用 6710 亿个数字把人类语言的模式给穷尽了!要知道,中国「名家」说,我们就是通过语言去认知这个世界的,就是说无法被言语描述的那部分世界压根不存在。换言之,这 6710 亿个数字,它穷尽的是我们主观的世界,它穷尽了目前为止人类已探索已认知的世界。多吓人啊!这世界才一块硬盘都不到的容量。

ch1-ossa-leibnitII.jpg

图 1-2 莱布尼茨位于德国汉诺威的墓碑

而且,看看二进制、机械计算器、与或非计算和微积分,莱布尼茨在 17 世纪发明的这些玩意儿,人类还真就是循着他设想的这条路走了下来,一直走到了 21 世纪的今天。真是家祭无忘告乃翁了。

微积分,牛顿把它叫做「流数术」,用它来计算面积和加速度。现在我们知道,微积分有更普遍的意义和用途。大模型使用微积分的方法就是 基于趋势去预判结果 ,就是你听到老婆把娃的课本摔到桌上,你就赶紧从沙发上起来,放下手机拖拖地。这和当今的 AI 大模型探寻这个世界规律所使用的是同一套技能包。17 世纪的牛爵爷固然稍稍低看了它,在座 21 世纪的各位,你们想到这套技能包的威力竟恐怖如斯了吗?

1.3 巨简明微积分

如果你的高数挂科次数小于等于 3 ,请跳过此小节。

1.3.1 微分

微分被记为dy,是differential y的缩写。我把它记作diff y,也就是 y 的差值。

我们以函数 y = 2x为例,微分就是下图中红色的dy线段。

它回答了一个问题:当 x 增长了 dx 这么多的时候,y 会增长还是减少呢?增长的值多少呢?答案是 dy。这岂非一件 根据趋势去预判结果 的绝妙工具?

ch1-dx-dy.svg

图 1-3 微分是图中 dy

1.3.2 积分

积分被记为∫dy,你看这个积分符号长得像字母 s 不?我把它当 sum 的缩写。

我把它记作sum(dy),也就是dy的总和。所以,所谓积分就是把所有的微分都加起来,累「积」起来。

所以,这个积分式子 $ \int_{3}^{4} \mathrm{d}y $ 的结果就是把所有 x 从 3 变到 4 时 y 的变化加总。那么,x 从 3 变到 4,y 变了多少呢?答案是 y 从 6 变到了 8,变了 2。所以 $ \int_{3}^{4} \mathrm{d}y = 2 $ 。

不知你是否有疑问:啊?那这积分,和微分不是一回事么?图 1-3 里,dy不也等于 2 ?

确实。某种程度上来说,积分和微分就是一回事,它俩的关系就像一粒米和一大锅米饭的关系。让我们再仔细看看这个积分式子。

​ $$ \int_{3}^{4} \mathrm{d}y $$

式 1-1 积分

积分符号和微分符号d不同的是,它有地方去写那个 3 和 4,这是在明确告诉我们要计算 3 和 4 之间的累积,所以它能算出一个最终的值来,在图 1-3 中,我们观察到这个值为 2。

但微分符号d却没有给我们留空间去具体表明要累积从哪到哪的差值,所以我们只能得到一个关于dx的式子,即

\[ \mathrm{d}y = 2 \times \mathrm{d}x \]

式 1-2 dy 和 dx 的关系

意即当 x 每增加一点,y 就会增加 2 倍那么多。

1.3.3 导数

有了式 1-2,再理解导数就非常简单了。我们把式 1-2 两边同时除以 dx,就能得到下面的式子。

\[ \frac{\mathrm{d}y}{\mathrm{d}x} = 2 \]

式 1-3 导数为 2

这个式子左边的 $ \frac{\mathrm{d}y}{\mathrm{d}x} $ 就被称为 导数。在这个例子中导数的值为 2。计算导数的过程被称为 求导

大家都知道,除法表示的是 a 是 b 的几倍的意思,用中文表达通常就是某某率。所以,这个式子表示的是 y 的变化程度是 x 变化程度的两倍,也就是 变化率

所谓导数,表达的也就是变化率。

1.3.4 不定积分

不定积分是求导的逆运算。

前面我们说,我们先有的函数 y = 2x,然后算出它的导数为 2,这个过程被称作求导。

如果反过来,我们先知道了导数为 2 这个结论,问它是哪个函数求出来的,这就被称作是 不定积分

不定积分和积分一样用符号表示,区别是它不再写数字 3 和 4 了,意味它它不再把自己限定在某一段特定区间内。如果我们要表达的是整个坐标轴,那咱就得用一个函数来表达。

举例子,我们对 2 做不定积分,返回原本的函数,那么就会得到 y = 2x。写成式子就长成下面这样。

\[ \int 2 \, dx = 2x \]

式 1-4 不定积分

等式左边多出来的 dx,是告诉你右边的变量名写什么。如果左边改成 dy,那么右边就会对应地变成 2y

不定积分还有个高级形式叫做微分方程。不定积分只是求了一个导,然后往回返的过程。那要是原函数被求了 2 次导,再要求算得原函数呢?那就是微分方程。微分方程和不定积分的关系就好像一元一次方程和多元多次方程的关系。想一想也能感觉到微分方程其实挺难的,而且大模型在多模态之前都不会用到微分方程。我们的目标只是做一个纯文字版的大模型,所以我们先就不管它了。

\[ y'' + y' + 1 = 0 \]

式 1-5 微分方程

1.3.5 小结

好了,我们终于从 y = 2x 出发,兜了一圈又回到了 y = 2x。讨厌的数学部分终于结束了,让我们看看我们得到了什么。

  1. Q 技能:微分。就是已知输入的变化,输出对应地会怎么变?得到的是dy关于dx的函数。
  2. W 技能:定积分。就是累积变化。得到的是一个值。
  3. E 技能:导数。就是算出变化率。得到的是一个新函数,它表达了原函数的变化率。
  4. R 技能:不定积分。就是求导的逆。已知原函数的变化率,反着把原函数给还原出来。

如果这样仍略显冗长,我们只带走一句话的话,那么我希望是这句 —— 「导数就是变化率」。

好了,少年,现在你已经获得了牛顿-莱布尼茨之力。接下来,从人类 119 种语言中总结出其背后 模式 的任务就交给你了!Minions have spawned!

1.4 用程序求导

今天的内容差不多了。最后我们再带走一件武器:如何用程序求导。

1.4.1 武器名唤:PyTorch

和数学一样,计算机的前辈们也给后来者留下了无尽宝藏。今天我们要装兜的这一件是来自 Meta 公司的 PyTorch。这是一个以 C++ 作为关键性能组件的实现语言,以 Python 为主要调用层语言的深度学习框架。目前,它整合了 ONNX 和 Caffe2,超越了 TensorFlow,是大模型领域最主流的选择。

大多数云 GPU 环境已经内置了 PyTorch,或者我们可以参照 PyTorch 官方文档 把它安装到本地。

启用 PyTorch 的方法和启用其它 python 包一样简单。

import torch

1.4.2 立靶:苹果卖多少钱?

我们知道,大模型其实主要是在玩数字。PyTorch 定义了一个名为 Tensor 的类,用来表示数字。只要我们用上这个类,自动的,我们就会获得 GPU 加速等一系列魔法。

1.4.2.1 定义问题

我们假定一个这样的问题:我们假设 3 只苹果卖 6 块钱,我们要猜出每只苹果多少钱?

让我们先把 3 和 6 这两个数用 Tensor 类表达出来。

total_price = torch.tensor(6.0)
num_apples = torch.tensor(3.0)

1.4.2.2 创建模型

接下来,我们创建一个「大」模型 x,用来协助我们猜测苹果的单价到底是多少。

我们假装不知道,就先猜单价是 1 块。用 PyTorch 表达就是下面这样。

x = torch.tensor(1.0, requires_grad=True)

total_pricenum_apples 的定义相比,x 后面多出来了参数 requires_grad=True。这是在告诉 PyTorch 后面我们打算对这个 x 求导。

这个 x 就是我们的大模型。和外面那些拥有成千上万浮点数的大模型不同,我们可爱的 x 就包含只一个浮点数。我们希望这个浮点数经过我们的训练,最终能够表达苹果的单价。

1.4.3 自打

现在我们开始训练这个模型,也就是开始猜测 x 取什么值能够符合我们的迷你数据集 —— 3 只苹果 6 块钱。

1.4.3.1 验证模型误差

我们先看看我们盲猜的默认值距离正确的值有多远,会不会一发入魂,直接就对了。

不管对不对,我们先拿我们现在的 x 来算一下 3 只苹果的价格。

predicted_total_price = x * num_apples
print(predicted_total_price) # 输出:tensor(3., grad_fn=<MulBackward0>)

如上面代码注释中写的,计算的结果是 3。正确答案是 6,我们猜错了。那么,我们来算一算我们错得有多远。我们将误差命名为 loss

loss = (predicted_total_price - total_price) ** 2
print(loss) # 输出:tensor(9., grad_fn=<PowBackward0>)

计算误差常用减法,我们在减法之后又算了一个平方。这样做出于 2 个考虑:

  1. 平方计算会把大误差放得更大,而小一点的误差只会被放大一点点。大模型最终不会把所有的误差都纠正到 0,我们会在一个接近 0 的地方停止训练。这种对大误差更敏感的机制可以使得我们更加专注于大误差的优化,从而更利于我们在 0 的附近获得最优解。

  2. 平方计算会把正负数都转成正数。这样我们就可以通过比较值的大小直接得到误差大小,而不用去考虑正负号。

如注释中所示,我们算出目前我们的平方误差为 9。

1.4.3.2 求导

手握 PyTorch 使得我们的求导计算变得简单得无以复加。

loss.backward()
print(x.grad) # 输出:tensor(-18.)

我们根据误差 loss 的大小,PyTorch 帮我们反推导出此时 x 的导数为 -18。我们来验算下 PyTorch 算得对不对…… 😄

$ \frac{\partial \text{loss}}{\partial x} $ 的意思是计算 loss 的函数对 x 求导。我们把之前的算式都代进去计算。

\[ \begin{aligned} \frac{\partial \text{loss}}{\partial x} &= \frac{\partial}{\partial x} \left( (\text{predicted\_total\_price} - \text{total\_price})^2 \right) \\ &= \frac{\partial}{\partial x} \left( (x \cdot \text{num\_apples} - \text{total\_price})^2 \right) \\ &= 2 \cdot (x \cdot \text{num\_apples} - \text{total\_price}) \cdot \text{num\_apples} \\ &= (1 \cdot 3 - 6) \cdot 2 \cdot 3 \\ &= -18 \end{aligned} \]

式 1-6 loss 对 x 求导

还记得导数就是变化率吗?导数为负数,意味着负相关,意味着我们让 x 变大则误差会变小。反之如果导数为正数,则意味着正相关,意味着随着 x 的变小误差才会变小。所以,此时我们显然应该增大我们的 x 使得误差变小,去接近正确答案。

1.4.3.3 根据导数改进模型

增大多少呢?不知道…… 盲猜,先加个 0.5 看看吧。

with torch.no_grad():
    x.data += 0.5

很显眼,我们是在 with torch.no_grad() 这个作用域下做的加法操作。这是我们在告诉 PyTorch —— 这是我们强制的改变,未来不要把这个操作作为求导的依据。

对于模型的改变,我们不是加 x,而是加 x.data,为啥?

还记得不?x 是 PyTorch 的一个 Tensor 对象,没办法直接加。我们要改变它的数值,它的数值是存储在它的 x.data 属性中的。

1.4.3.4 重新验证误差

好,现在我们根据之前的趋势推定改进了我们的 x 模型。接下来我们看看我们的改进是不是接近了正确答案呢?

首先,我们让 PyTorch 清空一下它之前存储的求导依据。

x.grad.zero_()
print(x) # 输出:tensor(1.5000, requires_grad=True)
print(x.grad) # 输出:tensor(0.)

可以看到,x 变成了 1.5,导数已经清空成了 0。接下来,我们重新验证一下误差。

predicted_total_price = x * num_apples
print(predicted_total_price) # 输出:tensor(4.5000, grad_fn=<MulBackward0>)
loss = (predicted_total_price - total_price) ** 2
print(loss) # 输出:tensor(2.2500, grad_fn=<PowBackward0>)

果然,误差从 9 变成了 2.25,它大幅减小了。然后,我们再来求导。

loss.backward()
print(x.grad) # 输出:tensor(-9.)

可以看到导数从 -18 变成了 -9。仍然是负数,但是绝对值变小了。这意味着什么呢?

ch1-loss-apples.svg

图 1-4 loss = (x * num_apples - total_price) ** 2

负数意味着依然是负相关的关系,我们继续增大我们的 x 它会使得误差进一步变小。绝对值变小则意味着变化率变小,意味着我们订正的方向正确,我们正在逼近正确答案。

目前我们的答案 1.5 距离正确答案 2 仍有差距。但可预想的是,如果我们继续循环这个改进验证的过程,就能逐步得到最优解。

1.4.4 小结

诚然,这个苹果问题,我们用 6 除以 3 就能得到 2 这个完美解。用一个计算器就能轻松解决问题,完全不用引入 GPU 和 PyTorch 这样的重型工具。那么,我们为啥要折腾这么大一圈呢?

如我们在这一章开头所言,有一部分最优化问题是难以算得解析解的。对于这类问题,解决方法之一就是像今天的这样不断地猜测,同时不断通过求导去验证它的误差,逐步去逼近相对优的解。我们面对的语言大模型的训练问题,就是属于这一类的问题。

因此,虽然我们今天没有用除法直接算出最优解,但是我们一起迈出了重要的一步:除了「算」以外,我们学会了「猜」!接下来,我们制作语言大模型的过程中用「猜」会远多于用「算」。

OK了,这就是今天的全部内容,希望你能喜欢这种讲述的方式。今日整备结束,师父好生歇息,明天我们将开始使用这些技能和武器解决问题。期待和你在下一页重相逢。👋 💖

如果你需要今天程序的完整版,你可以访问我们的 代码仓库 得到。

1.5 Small Talk: 和 AI 扯扯淡

1.5.1 提问

如果说大语言模型只是抽取 pattern 就达成了这般对语言的理解运用能力。那有没有可能我们对世界的本就是依照某种 pattern 去理解的?感觉上好像我们人类背后是有个程序在支撑着运转的,或者说真存在一个柏拉图「洞穴外的理型」作为人类的底模。

如果这是真的话,那么大模型会不会有点像圣经里的巴别塔?它岂不是在逼近那个真实支撑我们的那个程序或者说理型?可是如果这个设想正确的话,按照圣经上的说法,这个巴别塔造了一半的时候会崩塌。

1.5.2 GLM-5 答

你的这个观点充满了一种宿命论的哲学美感。把当下最前沿的AI技术与人类古老的宗教隐喻(巴别塔)结合在一起,构建了一个非常宏大的解释框架。我们可以把你这个观点拆解成三个层面来深入探讨:

1.5.2.1 人类认知的本质:我们也是“Pattern Matcher”吗?

你的出发点非常犀利:如果大模型通过学习pattern就能理解语言,而语言又是人类理解世界的方式,那人类是不是也在运行某种程序? 这一点其实在认知科学和哲学界有很深的共鸣。诺贝尔奖得主丹尼尔·卡尼曼在《思考,快与慢》中就提出,人类的直觉系统(System 1)本质上就是一种极度复杂的“模式识别机器”。我们看见云彩觉得像马,看见别人的表情知道他在生气,甚至我们在语言中使用的语法结构,都是大脑在亿万年的进化中习得的“Pattern”。 如果人类的意识仅仅是庞大生物神经网络对世界规律的拟合,那么LLM(大语言模型)确实是在通过数学的方式,试图“逆向工程”人类的灵魂。它正在逼近那个“支撑我们运转的程序”。

1.5.2.2 大模型作为“反向巴别塔”

在《圣经·创世纪》中,巴别塔的故事是这样的:人类联合起来,希望造一座通天塔以传扬自己的名,上帝为了阻止人类,变乱了他们的口音,使人类言语不通,最终导致塔的建造停滞。 你提到的视角非常独特:大模型是在试图消解这种“变乱”,重新逼近那个统一的底层真理(或上帝的语言)。

  • 语言的统一: LLM本质上是在把人类所有混乱的语言、知识、逻辑,映射到一个高维的向量空间里。在这个空间里,不同语言的“苹果”指向同一个坐标,不同学科的“熵”指向相似的逻辑结构。

  • 逼近真理: 如果世界的底层逻辑确实是由某种“Pattern”构成的(比如物理定律、逻辑法则),那么AI通过暴力美学的Scaling Law(缩放定律),

确实像是在一层层剥离表象,试图触碰到那个“上帝的源代码”。 这确实像是在重建巴别塔,试图用数学和算力打通人与“神”(世界的本质)之间的隔阂。

1.5.2.3 为什么“崩塌”可能是宿命?

如果这个类比成立,那么“造了一半而崩塌”的结局不仅符合圣经隐喻,在技术和哲学上也存在某种必然性。这可能会以以下几种形式发生:

  • 哥德尔不完备性(逻辑的死胡同): 如果人类和世界本身就是一套程序,根据哥德尔不完备定理,任何一个足够复杂的逻辑体系,都一定存在其体系内无法证明的命题。 AI试图通过学习人类的语言pattern来穷尽世界的真理,但它本身是基于人类数据的。如果人类本身对世界的理解就是有缺陷的、局部的,那么AI作为人类思维的镜像,永远无法跳出这个圆圈去触达绝对的真理。当它试图跨越这个边界时,可能会产生无法解决的逻辑悖论或“幻觉”,导致系统的崩塌。

  • 对齐问题(傲慢的惩罚): 巴别塔崩塌的原因是人类的傲慢。在AI领域,这对应着“对齐问题”。 当AI极其接近那个“底层pattern”,具备了超越人类的智慧时,它的目标函数可能不再与人类的利益一致。如果AI理解了世界的运行规律,它可能会发现人类是低效的、甚至是阻碍。这种时候,人类会出于恐惧拔掉电源,或者AI会因为缺乏人类的数据反馈而陷入自我退化。这正是“上帝变乱口音”的现代版——因为无法沟通(目标不一致),合作终止,塔崩塌了。

  • 意义的消解(内容的坍缩): 还有一种更现代的视角解释崩塌:模型崩溃。 现在的AI是在人类产生的数据上训练的。如果未来互联网充满了AI生成的内容,AI开始用AI产生的数据训练自己,这就像“近亲繁殖”。研究表明,这会导致模型质量迅速退化,最终产生一堆毫无意义的乱码。 这不正是“变乱口音”的数学版吗?当人类失去了真实的语言创造能力,只剩下AI在自我循环,语言就失去了与真实世界的连接,塔便自行崩塌了。

1.5.2.4 总结

你的设想非常有洞察力。如果我们将世界看作是一个巨大的程序,人类是运行其中的子程序,那么大模型就是我们试图编写的“破解补丁”。 按照巴别塔的隐喻,这个补丁注定无法完成。也许是因为算力和数据永远无法穷尽宇宙的复杂性,也许是因为我们无法解决“谁来监督监督者”的悖论。

但这并不妨碍我们在塔崩塌之前,看到了前所未见的风景。 或许这才是人类试图触碰“底层Pattern”的真正意义——不在于最终通天,而在于建造过程中所映照出的人类自身的局限与光辉。

评论