跳到正文
约 6 分钟

LLM 原理入门:从「猜下一个字」到 Transformer

1 先破除一个直觉:它不懂语言,它只会接龙

你在手机输入法中,一定会注意到:敲进去「今天天气真」,候选栏里第一个词永远是「好」。

输入法在干的事情只有一件——根据你前面打出来的字,猜你下一个字想打什么。它猜得又快又准,是因为它统计过海量文本里「今天天气真」后面出现过什么,「好」出现的次数压倒性得多。

大语言模型(LLM)做的事情,本质上和这个输入法是同一件事。区别只在三个地方:

  1. 它读过的文本多得多——不是一本语料库,而是整个互联网量级。
  2. 它能同时记住的前文长得多——不是一句话,而是几十万字。
  3. 它猜的不只是「词」,任何字符序列它都能续。

就这么简单?就这么简单。LLM 的全部核心任务,就是预测下一个 token 是什么。 术语叫「自回归语言模型」(autoregressive language model)。

写成一个数学表达式:

P(wn∣w1,w2,…,wn−1) P(w_n \mid w_1, w_2, \dots, w_{n-1})

读作「在已知前面 n-1 个词的前提下,第 n 个词的概率分布」。注意它输出的不是一个词,而是一张概率表——所有候选词各自的概率。

举个例子。给它输入:

成都的火锅

模型内部输出的可能长这样(示意,不是真实数字):

候选下一个 token 概率
很 0.31
真 0.18
好吃 0.09
是 0.06
店 0.03
……(词表里还有 15 万个) ……

它挑一个出来,比如「很」,然后把「很」接到句子末尾,变成「成都的火锅很」,再猜下一个。如此循环,直到它吐出一个表示「说完了」的特殊 token。

所以,当你看到 LLM「写出一篇一千字的文章」,实际发生的事情是:它把「猜下一个字」这个动作,重复了一千多次。

这个认知很重要。它意味着后面所有看起来神奇的能力——翻译、写代码、推理——都必须被解释成同一件事的副产品:要想把下一个词猜得足够准,它不得不学会很多东西。

2 第二步之前的第零步:文字得先变成数字

计算机不认识字,只认识数字。所以再谈「猜」之前,得先解决一个前置问题:一段中文怎么变成一堆数字。

这一步叫 分词(Tokenization),产出的最小单位叫 token。

2.1 为什么不是按字切,也不是按词切

两种最朴素的想法都会撞墙:

  • 按单字切:词表很小(常用汉字几千个就够),但序列太长。而且单字丢掉了「火锅」这种词的整体语义——「火」和「锅」单独看,跟「火锅」这个意思是两回事。
  • 按词切:符合直觉,但词的边界很难定,而且词是无限的。新词、人名、品牌名、网络梗每天都在冒,词表永远跟不上,遇到没收录的词就只能标记成一个 [UNK](未知词),信息直接丢了。

工业界的答案是第三条路:BPE(Byte Pair Encoding,字节对编码)。

思路非常朴素,像是一个不断找规律的压缩算法:

  1. 一开始,所有文本都拆成最底层的最小单位(字节或字符)。
  2. 统计整个语料库里,哪两个相邻单位一起出现的次数最多。
  3. 把它们合并成一个新的单位,加进词表。
  4. 重复第 2 步和第 3 步,直到词表达到目标大小(比如 10 万个)。

举个例子,假设语料库里这样一组词反复出现:

low  lower  lowest  newer  newest

初始状态全都拆成字符,然后算法会发现 e 和 r 老是挨在一起,于是合并成 er;接着发现 est 老是挨在一起,合并成 est。最终得到:

low  low|er  low|est  new|er  new|est

高频的整词被保留成单个 token,低频的长词被拆成「词根 + 词缀」。词表大小可控,同时永远不会遇到「未知词」——最坏情况下退化成按字节切。

2.2 真实的中文切出来是什么样

GPT 系的模型词表里中文占比不高,所以中文经常被切得比较碎。举个例子,「成都的火锅很好吃」在某个词表下可能是:

成都 | 的 | 火锅 | 很 | 好吃

看着挺合理,但换成生僻一点的词就会碎掉:

饕餮 | 之 | 宴

更极端的情况,一个不常见的字可能被拆成两个字节 token。这意味着模型看到的从来不是「字」,而是 token 的编号。

2.3 一个著名的推论:它数不清 strawberry 里有几个 r

「strawberry 里有几个 r?」这个问题曾经难倒过一大批模型。原因不在「数学能力差」,而在 tokenization。

模型拿到这个词,看到的不是 s-t-r-a-w-b-e-r-r-y 这十个字母,而可能是:

straw | berry     (或者 str | aw | berry,取决于词表)

在它的视野里根本不存在「字母」这个层级。要它数字母,等于让一个只会认整块积木的人去数积木上的花纹——它得先学会把 token 拆开看,而这件事在训练数据里并不常见。同理,模型做多位数算术容易错位、倒着念单词容易出错,根子都在这里。

2.4 动手看一眼分词结果

如果本机有 Python 环境,装一个 tiktoken 就能亲手看分词结果:

import tiktoken

enc = tiktoken.get_encoding("cl100k_base")   # GPT-3.5/4 系列用的词表
text = "成都的火锅很好吃"

ids = enc.encode(text)
print(ids)                                     # 一串整数编号
print([enc.decode([i]) for i in ids])          # 每个编号对应的原文片段
print("token 数量:", len(ids))

你会看到 len(ids) 明显大于「成都的火锅很好吃」这 8 个字的直觉预期——因为中文在这里通常一个字占 0.6 到 1 个 token 不等,标点和生僻字更费。

这也是为什么 API 按 token 计费,以及为什么「上下文长度 128K」说的其实是 128K 个 token 而不是 128K 个汉字。

3 从编号到向量:让数字带上意思

有了 token 编号,我们得到一个整数序列,比如 [30589, 5, 47832, 256, 10293]。

但编号本身是没有语义的。它只是个字典里的行号——就像学号 7 和学号 8 之间没有「多一个」的关系,token 编号 30589 和 30590 之间也没有任何联系。你不能把编号直接拿去算加减乘除,那没有意义。

所以要再做一次映射:把每个编号查表转换成一个高维向量(比如 4096 个浮点数)。这叫 词嵌入(Embedding)。

具体做法是一张巨大的查找表,尺寸是 词表大小 × 向量维度,比如 100000 × 4096。第 30589 号 token 的向量,就是这张表的第 30589 行。

token 编号 30589  ──查表──▶  [0.021, -0.457, 0.883, ..., 0.156]   (4096 个数字)

3.1 为什么换成向量就有意义了

因为这张表本身也是训练出来的。训练过程中,模型会发现:凡是用法相似的词,如果它们的向量也相似,就更容易把下一个词猜准。于是训练自然会把这些向量推到「按语义聚簇」的位置上。

经典例子(来自早期的 word2vec,后来被反复验证):

king - man + woman ≈ queen

「国王」减去「男人」加上「女人」,最接近的向量是「女王」。这意味着这个向量空间里,「性别」这个维度是可加减的——语义真的被编码进了几何位置里。

换个角度看,向量就像给每个词在一张地图上定了坐标,意思相近的词住得近。这张地图的特殊之处是它有几千个维度,人脑想象不了,但数学处理起来毫无障碍。

3.2 但这个向量是「死」的

这里有个关键限制必须先说清楚:查表得到的向量和上下文无关。

「苹果」这个词,在下面两句话里查表拿到的向量一模一样:

我今天买了两个苹果。
苹果发布了新款手机。

第一句里苹果是水果,第二句里苹果是公司。如果向量不变,模型怎么区分?答案是:光靠 embedding 分不了,得分靠后面那几十层 Transformer 去把这个向量「改造成带上下文的版本」。

这就是下一节要讲的核心机制。你可以把 embedding 理解成「每个词的出厂默认含义」,而注意力机制负责根据具体语境把它调整成「此刻的含义」。

4 注意力机制:让每个词看看别人

这是 Transformer 架构里唯一真正新颖的东西,也是整个领域的分水岭。2017 年那篇论文的标题直接就叫《Attention Is All You Need》。

4.1 从一个歧义句说起

看这句话:

那只动物没有过马路,因为它太累了。

「它」指的是什么?——是动物。因为累的是动物,不是马路。

再看:

那只动物没有过马路,因为它太宽了。

同一个「它」,这次指的是——马路。因为「宽」是用来形容马路的。

两句话里「它」这个词的形式完全一样,但含义截然不同。人脑靠上下文瞬间判断出来。注意力机制要解决的,就是让模型也具备这个能力。

更抽象地说:处理「它」这个位置的信息时,模型需要去看看句子里的其他词,判断哪些和自己相关,然后把相关信息取过来。

4.2 Q、K、V:一个检索系统的三个角色

实现这个「看别人」的动作,用的是三个向量,名字叫 Query、Key、Value。

一个很好用的类比是图书馆找书:

  • Q(Query,查询):你此刻的需求。比如你脑子里想的是「有没有讲宋代历史的书」。
  • K(Key,键):每本书书脊上的标签。比如「中国史 · 宋代」。
  • V(Value,值):书里的实际内容。

流程是:你拿着自己的 Q,去和书架上每一本书的 K 做匹配,匹配度高的书,你就多拿一点它的 V。最后你把拿到的内容综合起来,就是你这次检索的收获。

在注意力的场景里:

  • 句子里的每一个词,都会生成自己的 Q、K、V 三个向量(通过三个训练出来的权重矩阵从 embedding 变换得到)。
  • 处理第 5 个词时,用它的 Q 去和前 4 个词的 K 分别打分,得到 4 个权重。
  • 再用这 4 个权重,对前 4 个词的 V 做加权求和,得到一个融合了上下文的向量。

用刚才的句子走一遍。假设句子是:

那只 | 动物 | 没有 | 过 | 马路 | , | 因为 | 它 | 太 | 累 | 了

处理「它」这个词时的注意力权重可能是:

关注的词 权重
动物 0.62
马路 0.11
那只 0.08
累 0.07
其他 0.12

「它」的向量因此被「动物」的内容主导了。而在「太宽了」那个句子里,「马路」的权重会反转上来。同一个「它」,因为权重不同,最终得到的向量完全不同。

4.3 公式长什么样

上面这个过程,写成公式只有一行:

Attention(Q,K,V)=softmax(QKTdk)V \text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V

拆开看,分四步:

  1. $QK^T$:把每个词的 Q 和每个词的 K 两两点积,得到一个分数矩阵。分数高代表「这两个词相关」。
  2. 除以 $\sqrt{d_k}$:$d_k$ 是 K 向量的维度。这一步是数值稳定性的考虑——维度高的时候点积结果会很大,经过 softmax 之后会变成「一个 1、其余全 0」的极端分布,梯度就没了。除以维度的平方根能把分布压回合理范围。
  3. softmax:把分数变成权重,全部为正,加起来等于 1。这就是上面表格里那组权重的来源。
  4. 再乘 $V$:按权重把各词的内容加权求和,得到输出。

4.4 一个必须有的限制:因果掩码

上面的描述里有个漏洞:如果每个词都能和所有词互动,那处理第 5 个词时它就能看到第 6、7 个词。这在生成任务里是不允许的——预测下一个词的时候,不能偷看答案。

解决办法叫因果掩码(causal mask):把注意力分数矩阵的上三角部分(未来位置)全部设成负无穷,softmax 之后那些位置的权重就变成了 0。

效果是注意力矩阵变成了一个下三角形状:

       那  只  动  物  没  有  过
那     ✓  -   -   -   -   -   -
只     ✓  ✓   -   -   -   -   -
动     ✓  ✓   ✓   -   -   -   -
物     ✓  ✓   ✓   ✓   -   -   -
没     ✓  ✓   ✓   ✓   ✓   -   -
有     ✓  ✓   ✓   ✓   ✓   ✓   -
过     ✓  ✓   ✓   ✓   ✓   ✓   ✓

每个词只能看自己和左边。这也解释了为什么语言模型能并行训练——因为掩码已经保证了「不偷看未来」,所以整句话可以一次性喂进去同时算,每个位置都在做一次合法的预测。

4.5 多头注意力:不只问一个问题

实际模型里不会只做一次注意力,而是同时做好几组,叫多头注意力(Multi-Head Attention)。GPT-3 用了 96 个头。

为什么?因为一段文本里的关系是多重的,一组注意力不可能同时兼顾:

  • 某一个头可能专门盯着指代关系:把「它」连到「动物」。
  • 另一个头可能盯着语法结构:把修饰语连到被修饰的名词。
  • 第三个头可能盯着位置邻近:只看紧挨着的几个词。
  • 还有的头盯时态一致性、动词搭配等等。

类比一下:如果让 8 个记者去采访同一场发布会,一个负责追问数据、一个盯现场反应、一个关心背景、一个看词锋……最后汇总的报道会比一个人写的丰富得多。

多头注意力的做法是把维度切开:假设模型维度是 4096,用 32 个头,那每个头负责 128 维。每个头在自己的 128 维空间里独立做注意力,最后把 32 个结果拼回 4096 维,再过一层线性变换。

4.6 位置编码:为什么必须有

注意力有个副作用:它本质上是个加权求和,对顺序不敏感。

什么意思?输入「我 打 你」和「你 打 我」,如果不加额外信息,注意力看到的只是三个词的集合,算出来的结果一样——因为 Q 和 K 的匹配不看位置。但这俩意思完全相反。

所以要显式地把位置信息注入进去,这就是位置编码(Positional Encoding)。

  • 早期做法是给每个位置算一组正弦、余弦值,加到 embedding 上。
  • 现在主流(Llama、Qwen、DeepSeek 等)用的是 RoPE(Rotary Position Embedding,旋转位置编码):不加法,而是在算注意力时把 Q 和 K 向量按位置角度旋转,让「两个词的相关性」自然带上「它们相距多远」的信息。

RoPE 有个额外的好处:它对相对距离的建模比较自然,这让模型在训练时见过 4K 长度、推理时外推到更长上下文变得相对容易一些(虽然外推仍然是难题)。

4.7 一句话小结注意力

注意力的作用,是让每个 token 的表示,根据它和其他所有 token 的关系被重新计算一遍。 词与词之间的信息在这里流动,语境在这里被注入。

5 一层 Transformer 里还有什么

注意力是主角,但不是独角戏。一个完整的 Transformer 层,由两个子层加两条「辅助线」组成。

5.1 前馈网络:注意力之后的加工车间

注意力做完信息交换之后,每个 token 的向量会进入一个前馈网络(FFN,Feed-Forward Network)。

前面说注意力是「开会同步信息」,FFN 就是「各自回工位消化」——每个 token 独立地过同一个两层全连接网络,中间维度会放大(通常是模型维度的 4 倍左右,现代模型用 SwiGLU 之类结构后大约是 2.7 倍),然后再压回去。

有意思的是,FFN 往往占了模型参数的绝大部分,大约是三分之二。有研究认为它更像一个「知识存储器」——模型记住的那些事实性知识(巴黎是法国首都、水的沸点是 100 度),很大程度存在 FFN 的权重里,而注意力更像是在负责「怎么把这些知识按需取出来组合」。

所以一个粗略的直觉是:注意力管调度,FFN 管存储。

5.2 残差连接和 LayerNorm:让几十层能训起来

这两条「辅助线」不起眼,但没有它们,深层网络根本训不动。

  • 残差连接(Residual Connection):每个子层的输出不直接传给下一层,而是加上子层的输入,也就是 输出 = 输入 + 子层(输入)。
  • 层归一化(LayerNorm):在子层前后把数值的分布拉回稳定的范围。

残差连接解决的是梯度消失问题。反向传播时梯度要从最后一层一路传回第一层,层数一多,梯度会越乘越小,最后趋近于 0,前面的层就学不动了。残差连接相当于给梯度开了一条「高速公路」——恒等路径上的梯度永远是 1,可以被原封不动地传回去。

5.3 整体数据流

一层 Transformer 的数据流大致是这样:

flowchart TB
    In["输入向量<br/>(每个 token 一个)"] --> LN1["LayerNorm"]
    LN1 --> MHA["多头注意力<br/>(信息交换)"]
    MHA --> Add1["残差相加"]
    In --> Add1
    Add1 --> LN2["LayerNorm"]
    LN2 --> FFN["前馈网络<br/>(信息加工,参数最多)"]
    FFN --> Add2["残差相加"]
    Add1 --> Add2
    Add2 --> Out["输出向量<br/>(维度不变,送去下一层)"]

注意最后一格:一层的输出维度和输入完全一样。这让「堆叠 N 层」在结构上变得非常自然。

5.4 为什么要堆几十层

GPT-3 有 96 层,Llama 3 70B 有 80 层。堆这么多层,是因为不同深度的层在处理不同抽象级别的东西。

用探测(probing)实验得到的经验结论,大致分层是:

  • 底层:词的形态、发音、基本语法特征。这一层看到「吃」和「饭」是两个 token。
  • 中间层:句法结构、指代消解、语义角色。这里「它 → 动物」的关系被建立起来。
  • 高层:抽象语义、世界知识、任务意图。到这里「这句话是在讽刺还是在陈述」才能被判断。

可以类比成读一篇外文文章:第一遍你在认字,第二遍你在理句子结构,第三遍你才懂言外之意。模型不能跳过这个层级,所以只能靠深度来堆。

每一层都在上一层的表示上做一次「重新理解」,层数越深,理解越抽象。

6 这些参数是怎么学出来的:训练的三板斧

到这里,模型的结构已经讲完了。但结构只是骨架——骨架里的那几十亿个数字(权重),才是真正干活的东西。这些数字怎么来的?靠训练。

主流 LLM 的训练分三个阶段,每一阶段解决一个不同的问题。

6.1 预训练:把互联网读一遍,只练一件事

目标:预测下一个 token。就这一个。

数据:几万亿个 token,来自网页、书籍、论文、代码仓库、百科……经过大量清洗去重之后拼成一条超长的文本流。

怎么做:把文本切成一段一段,每段都让模型去猜下一个 token,猜错了就调参数。

损失函数用交叉熵(cross-entropy)。直观理解:

正确答案是「很」。
- 如果模型给「很」的概率是 0.9  → 损失 = -log(0.9)  ≈ 0.105   (很好)
- 如果模型给「很」的概率是 0.1  → 损失 = -log(0.1)  ≈ 2.303   (需要重罚)
- 如果模型给「很」的概率是 0.01 → 损失 = -log(0.01) ≈ 4.605   (狠狠罚)

模型给正确答案的概率越低,罚得越狠,而且是越接近 0 罚得越离谱(这是 log 曲线尾部的特性)。训练的目标就是让这个平均损失降到最低。

调参靠反向传播:算出每个参数对损失「该负多少责任」,然后按梯度方向微调它。几十亿个参数,每一个都这么调,反复几百万步。

顺便提一句规模:GPT-3 的预训练动用了数千张 V100 显卡,跑了好几个月。这也是为什么只有少数机构能从头训大模型。

这一步最关键的一点,是它的副作用。

模型的任务只是猜下一个词,但为了猜得准,它不得不学会:

  • 语法(不然生成的是乱码,概率上就低)
  • 事实(「巴黎是法国」后面不接「首都」的概率极低)
  • 逻辑(「因为下雨,所以地面是___」只能是「湿的」)
  • 翻译(中英对照的语料读多了,它自然学到了映射)
  • 代码(代码的语法更严格,猜错的惩罚更大)

没有任何一条是显式教它的,全都是「把下一个词猜准」这个单一目标的副产品。 这是整个 LLM 领域最反直觉、也最迷人的地方。

预训练还有一个重要的工程结论:参数量和数据量必须匹配。Chinchilla 缩放定律指出,在算力预算固定的情况下,大约每个参数配 20 个 token 是最优的。这条定律纠正了早期「一味堆参数」的倾向——参数多、数据少,模型会「吃不饱」。

6.2 刚预训练完的模型,还不太能用

这一步结束时,你得到的是一个超级输入法:它会续写,但你没法跟它对话。

举个例子。你输入:

中国的首都是哪里?

一个纯预训练模型很可能续写成:

中国的首都是哪里?这是一个很多人都会问的问题。今天我们就来聊聊这个话题……

因为在它读过的网页里,这个句子往往出现在问答页面的标题上,后面跟着的是文章正文,而不是一个干脆的答案。它只会「接龙」,不会「回答」。

6.3 监督微调(SFT):从「续写」变成「回答」

第二步,用几万到几十万条人工标注的问答对去继续训练。数据长这样:

{
  "instruction": "把下面这句话翻译成英文:今天天气很好。",
  "response": "The weather is nice today."
}

这一步叫 SFT(Supervised Fine-Tuning,监督微调)。数据量相比预训练小了四五个数量级,但因为数据质量高、格式统一,模型很快就学会了新范式:用户写的是问题,我该给出答案。

同时,对话的「人设」也是在这一步塑造的——礼貌、简明、拒绝危险请求这些行为模式,很多来自 SFT 数据里的示范。

6.4 RLHF / DPO:学会「哪种回答更好」

SFT 有个天花板:它只能模仿示范答案,学不会「两个回答都不错,但哪个更好」。

所以还有第三步。以 RLHF(基于人类反馈的强化学习)为例,流程是:

  1. 让模型对同一个问题生成多个不同回答。
  2. 找人类标注员给这些回答排序(哪个最好,哪个最差)。
  3. 用这些排序数据训练一个奖励模型,让它学会「什么样的回答人类会更喜欢」。
  4. 用这个奖励模型当裁判,通过强化学习(通常用 PPO 算法)去优化主模型,让它产出得分更高的回答。

现在很多团队改用更简单的 DPO(Direct Preference Optimization),跳过奖励模型,直接用偏好数据优化,工程上省事不少。

这三步各解决了什么,值得对比着记:

阶段 数据 学到的东西 类比
预训练 几万亿 token 的通用文本 语言、知识、推理的底子 读完整个图书馆
SFT 几万条人工问答 「回答问题」的格式和人设 老师带着做例题
RLHF / DPO 人类对回答的排序偏好 「怎么答更让人满意」 反复批改,摸评分口味

有个数据很能说明第三阶段的价值:OpenAI 在 InstructGPT 论文里报告,一个 13 亿参数、经过 RLHF 的模型,人类评价竟然优于1750 亿参数的原始 GPT-3。对齐(alignment)带来的提升,比单纯堆参数更划算。

6.5 「7B 参数」到底指什么

经常听到「7B 模型」「70B 模型」,这个数字指的是模型里所有可调数字的总数。

  • 7B = 70 亿个参数
  • 用 16 位浮点数(fp16)存储,每个参数 2 字节,所以模型文件约 14 GB
  • 加上推理时的中间激活和 KV Cache,实际显存需求更高

为什么消费级显卡也能跑 7B 甚至更大的模型? 靠量化——把参数从 16 位压到 8 位或 4 位,精度损失可以接受,体积直接砍到四分之一。一个 4 位量化的 7B 模型大约 4 GB,一张 8 GB 显存的卡就能跑起来。

7 生成:文字是怎么一个个蹦出来的

训练讲完了,回到推理——也就是你按下回车之后发生的事。

7.1 自回归循环

流程是一个循环:

输入文本 → 分词 → 过 N 层 Transformer → 输出词表上的概率分布
   ↑                                          ↓
   └────────── 把选中的 token 接回输入 ←── 采样选一个 token

每转一圈,输出一个 token,然后把整个序列(包括刚生成的那个)重新喂进去,再来一圈。这就是自回归(autoregressive)。

顺便说一句,工程上不会真的「重新喂一遍」——那太浪费了。有两级优化:

  • KV Cache:单次生成中,已经算过的 K 和 V 存下来复用,把每步的复杂度从 $O(L^2)$ 降到 $O(L)$。
  • 前缀缓存(Prefix Cache):把多个请求里相同的开头(比如系统提示词)对应的 KV 存档,跨请求复用,省掉重复的 prefill。

这两个机制的细节,我单独写在《KV Cache 与前缀缓存:原理、对比与 DeepSeek 的实践》那篇里,这里不展开。

7.2 采样:为什么同一个问题,答案会不一样

模型最后输出的是概率分布,选哪个由采样策略决定。

贪心(greedy):永远选概率最高的那个。结果是确定的,但读起来会很死板、容易陷入重复——因为每一步都选「最安全」的,整段文本就会往最平庸的方向滑。

温度(temperature):在 softmax 之前,把所有分数除以温度值 $T$:

pi=ezi/T∑jezj/T p_i = \frac{e^{z_i / T}}{\sum_j e^{z_j / T}}

效果是调节概率分布的「尖锐程度」:

  • $T < 1$:分布变尖,高概率的更高,输出更确定、更保守。
  • $T = 1$:原始分布。
  • $T > 1$:分布变平,长尾词获得更多机会,输出更有创意,也更容易跑偏。

举个例子,输入「成都的火锅很」,温度不同结果差别很大:

温度 可能的下一个 token
0.1 几乎必然是「好」「辣」这类高频词
0.7 「好吃」「香」「正宗」「巴适」都可能
1.5 可能出现「像一场没排练过的合奏」这种意外表达

Top-k / Top-p(核采样):光调温度还不够,因为词表里有 15 万个词,长尾里全是垃圾选项。所以通常会先做截断:

  • Top-k:只保留概率最高的 k 个候选(比如 50 个),其余全部丢弃。
  • Top-p:保留累积概率刚好达到 p 的最小候选集(比如 p=0.9),候选数量动态变化——分布集中的时候候选少,分散的时候候选多。

这两个是搭配使用的:先温度调性格,再截断砍长尾,最后在剩下的候选里抽样。

所以「同一个问题问两次答案不一样」是正常现象。 想让它稳定,就把温度调到 0(等价于贪心)。

把上面的流程画出来:

flowchart LR
    A["N 层 Transformer 输出<br/>词表上的一串分数 logits"] --> B["除以温度 T<br/>调整分布尖锐度"]
    B --> C["Top-k / Top-p 截断<br/>砍掉长尾垃圾候选"]
    C --> D["在剩余候选中<br/>按概率抽样"]
    D --> E["得到一个新 token"]
    E --> F["追回到输入序列末尾"]
    F -.->|下一轮| A

8 三个最常见的困惑

8.1 幻觉是从哪来的

它不是在查资料,而是在做概率填空。

训练目标从头到尾只有一个:让生成的文本在概率上尽可能「像真实文本」。注意,是像,不是对。

后果是三件事:

  1. 在训练数据里,「某公司的 CEO 是 ___」这个位置后面,绝大概率跟着一个具体人名,而不是「我不知道」。所以模型会填一个人名——哪怕它记不准。
  2. 训练数据里「流畅」的文本占绝对多数,「承认无知」的表达极其罕见。所以模型倾向于给出语法通顺、语气自信的答案。
  3. 最关键的是,模型自己分不清「我记得」和「我编的」。在它内部,回忆某个事实和编造某个事实,走的是同一套机制、产生的是同样形态的概率分布。它没有额外的模块去检查「这个值是从哪来的、可靠吗」。

所以幻觉不是 bug,更像是概率模型的固有属性。缓解它的思路也因此都是从外部给约束,而不是指望模型自省:

  • RAG(检索增强生成):把相关资料先检索出来塞进上下文,让它「照材料答题」而不是凭记忆答题。
  • 工具调用:需要算数就调计算器,需要实时信息就查搜索,把「记忆任务」转成「执行任务」。
  • 要求引用:让它给出处,至少让你能验证。
  • 降低温度:减少抽样随机性。

顺便说,这也解释了为什么给足上下文时它往往很靠谱——一旦答案在上下文里明摆着,它的任务就从「回忆」变成了「抄写」,而抄写的准确率极高。

8.2 上下文窗口:它能「看见」多少

上下文窗口(context window) 指的是模型单次能处理的最大 token 数,包括你输入的提示词、多轮对话历史、以及它自己生成的输出。

这个数字直接决定了模型的「短期记忆」:

  • GPT-3 时代是 4K;
  • 现在主流模型普遍 128K 起步,长上下文模型做到 1M 甚至更多。

但要注意一个区分,它至关重要:

  • 参数里存的:模糊的、压缩过的世界印象。类似「你记得好像在哪本书里读到过」。容易记串、容易过时。
  • 上下文里给的:精确的、完整的当下材料。类似「这段话就摆在你面前」。

你问它「我上次跟你说的那个 bug」,它不可能知道——因为那不在参数里,也不在当前上下文里。你得告诉它。 这也正是 RAG 和长上下文之所以重要的原因。

另外,长上下文不是免费的:KV Cache 的显存占用随序列长度线性增长,1M token 的上下文会吃掉巨量显存,还会拖慢每一步解码。所以「支持 1M」和「在 1M 下仍然又快又好」是两回事。

8.3 知识截止日期

预训练数据有个时间边界,叫知识截止日期(knowledge cutoff)。在那之后发生的事,模型一无所知——除非你通过联网或检索把信息喂给它。

这就解释了一个常见怪象:模型对 2025 年的技术细节如数家珍,却完全没有听说过上个月发布的新版本。它不是忘了,是根本没见过。

9 全景图与总结

把整条链路串起来看一遍:

flowchart TB
    T["原始文本<br/>成都的火锅很好吃"] --> TK["① 分词<br/>切成 token 并编号"]
    TK --> EM["② 嵌入<br/>查表变成向量"]
    EM --> PE["③ 加位置信息<br/>RoPE"]
    PE --> TR["④ N 层 Transformer<br/>注意力交换信息 + FFN 加工信息"]
    TR --> LM["⑤ 输出头<br/>映射到词表的概率分布"]
    LM --> SM["⑥ 采样<br/>温度 + Top-p 选一个 token"]
    SM --> NEW["⑦ 新 token"]
    NEW -->|追加到序列,循环| TR
    SM -->|遇到结束符| DONE
    DONE["生成结束"]

    PRE["预训练<br/>预测下一个 token"] -.->|训练出| TR
    SFT["SFT<br/>学会回答问题"] -.->|训练出| TR
    RLHF["RLHF / DPO<br/>学会讨人喜欢"] -.->|训练出| TR

总结几条核心判断:

第一,LLM 的本体是一台概率接龙机。 所有能力——翻译、编程、推理——都是「把下一个 token 猜准」这个目标的副产品,而不是被显式设计出来的模块。想清楚这一点,很多看起来玄学的现象就有了解释。

第二,文本要先过一道 tokenization 的关卡。 模型不认识字母和汉字,只认识 token 编号。数不清 strawberry 里的 r、算不对多位数、计费按 token 计,根子都在这里。

第三,注意力是核心创新。 它让每个词都能根据和其他词的关系重新计算自己的表示,「它」指动物还是马路,由权重决定。多头机制负责同时捕捉多种关系,位置编码负责不让顺序信息丢失。

第四,深度负责抽象层级。 底层看词形,中层看句法指代,高层看语义和意图。堆层不是为了堆参数,是为了堆理解的层次。

第五,训练的三个阶段解决三个不同问题。 预训练给底子和知识,SFT 给格式和人设,RLHF/DPO 给品味。对齐带来的收益往往比单纯堆参数更大。

第六,推理是逐 token 的循环,采样策略决定性格。 温度调的是「保守与发散」,Top-p 砍的是长尾垃圾,「答案不稳定」是可预期的行为。

第七,幻觉来自训练目标本身。 它优化的是「像」,不是「真」,而且没有内省机制来区分回忆和编造。缓解手段都是从外部加约束——检索、工具、引用、降温。

理解了这七条,再回头看各种应用层的技巧(提示工程、RAG、微调、Agent),你会发现它们大多只是在回答同一个问题:怎么在「猜下一个 token」这个固定机器的输入和输出两端,做更聪明的工程。

评论