大模型如何理解你的话并生成回复

· 约 9 分钟读完

你在对话框里打了一句”今天天气怎么样”,按下回车。不到一秒,屏幕上开始一个字一个字地蹦出回复。

这中间发生了什么?模型是怎么”读懂”这句话的?又是怎么组织出一段通顺的回复的?

这篇文章从头到尾走一遍大模型的推理流程——模型已经训练好了,你问它一句话,它内部到底发生了什么。

分词:先把文字切碎

模型不认识”字”。它的最小处理单位是 token——一种介于字和词之间的片段。

拿”今天天气怎么样”举例,tokenizer 可能把它切成这样:

["今天", "天气", "怎么样"]

每个 token 对应词表里的一个整数 ID:

[8837, 25031, 48920]

这个切法不是按字典分词,而是用 BPE(Byte Pair Encoding)之类的算法,在大量文本上统计出来的高频子串。出现频率高的字符组合会被合并成一个 token,生僻的组合则被拆得更细。

中文通常一到两个汉字对应一个 token。英文里常见单词(like、the)往往整个词是一个 token,不常见的词会被拆成几段——比如 “tokenization” 可能变成 ["token", "ization"]

词表大小通常在 10 万量级。这张表在训练前就固定了,推理时不会变。

嵌入:从文字到高维空间

有了 token ID,下一步是把每个 ID 变成一个向量。

模型内部有一张巨大的嵌入矩阵,大小是 词表大小 × 隐藏维度,比如 100000 × 4096。每个 token ID 去这张表里查对应的那一行,取出一个 4096 维的向量。

为什么要变成向量?因为后续所有计算都是矩阵运算,只能作用于数字。而且向量空间有一个好处:语义相近的词,在空间里的位置也靠得近。“猫”和”狗”的向量距离,比”猫”和”经济”的距离近得多。

但光有词义不够。“我打他”和”他打我”包含完全相同的三个 token,意思却完全不同。模型需要知道每个 token 在序列中的位置。

现代模型普遍采用 RoPE(旋转位置编码):不是给每个位置加一个固定向量,而是把位置信息编码成向量的旋转角度。这样模型天然能感知两个词之间隔了多远,而且对训练时没见过的序列长度也有一定的外推能力。

经过嵌入和位置编码后,输入变成了一个矩阵,形状是 [序列长度 × 隐藏维度],比如 3 个 token 就是 [3 × 4096]

整个嵌入阶段的维度变化:

步骤输入变换输出
查嵌入表token ID(标量)嵌入矩阵 100000 × 4096向量 [4096]
3 个 token3 个 ID各自查表矩阵 [3 × 4096]
加位置编码[3 × 4096]RoPE 旋转变换[3 × 4096](形状不变,值变了)

注意力机制:让每个词看见所有词

这个矩阵接下来要穿过几十甚至上百层 Transformer 层。每一层做两件事:自注意力和前馈网络。

自注意力:谁跟谁相关

自注意力是 Transformer 的核心。对序列中的每个 token,模型做这几步:

  1. 把它的向量通过三个不同的线性变换,生成三个新向量:Q(查询)K(键)V(值)
  2. 用当前 token 的 Q 去和所有 token 的 K 做点积,算出一组分数——“我应该关注谁”
  3. 对分数做 softmax,归一化成权重(加起来等于 1)
  4. 用这些权重对所有 token 的 V 做加权求和,得到一个融合了上下文信息的新向量

以 3 个 token 的序列为例,维度变化如下:

步骤输入形状变换矩阵输出形状
生成 Q[3 × 4096]W_Q [4096 × 4096][3 × 4096]
生成 K[3 × 4096]W_K [4096 × 4096][3 × 4096]
生成 V[3 × 4096]W_V [4096 × 4096][3 × 4096]
Q · K 转置[3 × 4096] · [4096 × 3][3 × 3](注意力分数)
softmax[3 × 3][3 × 3](归一化权重)
权重 · V[3 × 3] · [3 × 4096][3 × 4096]

关键在第四行:Q 乘以 K 的转置后,输出是一个 [3 × 3] 的矩阵——每个 token 对其他所有 token 的注意力分数。这就是”每个词看见所有词”的数学含义。

举个具体的例子:处理”怎么样”这个 token 时,模型算出它对”天气”的注意力权重是 0.6,对”今天”是 0.3,对自身是 0.1。加权求和后,“怎么样”的新向量里就融入了”天气”和”今天”的语义。到这一步,模型已经把”怎么样”和”天气”关联起来了——它开始”理解”你问的是今天的天气怎么样。

多头注意力:从不同角度看关联

上面的过程不只做一次,而是并行做很多组(比如 32 组),每组叫一个”头”,使用不同的变换矩阵。

不同的头会学到关注不同维度的关系:有的头可能聚焦语法结构(主语和动词的对应),有的关注语义相似度,有的捕捉位置邻近性。最后把所有头的输出拼接起来,通过一个线性变换合并成一个向量。

前馈网络:加工和提取知识

注意力算完之后,每个位置的向量独立经过一个两层的全连接网络。通常第一层把维度膨胀到 4 倍(比如 4096 → 16384),经过激活函数后,第二层再压回原始维度。

步骤输入形状变换矩阵输出形状
第一层(膨胀)[4096]W_1 [4096 × 16384][16384]
激活函数[16384][16384]
第二层(压缩)[16384]W_2 [16384 × 4096][4096]

如果说注意力层决定了”关注哪些信息”,前馈网络就是对这些信息做深层加工。模型在训练中学到的大量事实性知识——谁是哪国总统、某个函数怎么调用——主要存储在前馈网络的参数矩阵里。

每一层末尾还有残差连接(把这一层的输入直接加到输出上)和层归一化,防止信号在几十层传递中消失或爆炸。

层层叠加

以上就是一个 Transformer 层做的全部事情。大模型通常堆几十到上百个这样的层。

每过一层,token 的向量表示就更”深”一些:浅层捕捉词法和简单句法,中间层处理语义关系,深层出现更抽象的推理模式。穿过所有层之后,每个位置上的向量已经不只代表那个词本身,而是编码了整个输入序列的上下文信息。

输出:从向量到概率分布

穿过所有 Transformer 层后,拿最后一个 token 位置的向量——它已经”看过”了前面所有内容。

这个向量乘以一个 [隐藏维度 × 词表大小] 的矩阵(叫输出头),得到一个长度等于词表大小的向量。里面每个数字对应词表中一个 token 的”得分”,术语叫 logits

步骤输入形状变换矩阵输出形状
取最后位置的向量[3 × 4096][4096]
乘输出头[4096]W_out [4096 × 100000][100000](logits)
softmax[100000][100000](概率分布)

对 logits 做 softmax,就变成了概率分布——10 万个 token 各自有一个概率:

"晴"   → 0.15
"不"   → 0.12
"我"   → 0.08
"很"   → 0.07
"今"   → 0.05
...(剩余 10 万个 token 都有一个概率)

到这一步,模型的工作本质上就是:给定前面的所有内容,告诉你下一个 token 各有多大可能是什么。

采样:从 10 万个候选里选一个

有了概率分布,怎么选下一个 token?直接选概率最高的看起来最合理,但实际效果往往不好——生成的文本会很机械,容易陷入重复。所以需要采样策略。

贪心(Greedy) 最简单:每次选概率最高的 token。确定性最强,但最死板。

Temperature 在 softmax 之前用一个温度系数缩放 logits。温度低(比如 0.3),概率分布更尖锐,高概率 token 更容易被选中,输出更确定。温度高(比如 1.2),分布更平坦,低概率 token 也有机会出头,输出更有”创意”,但也更容易跑偏。

Top-p(核采样) 把 token 按概率从高到低排列,累加直到总概率达到 p(比如 0.9),只在这个子集里采样。既保证了多样性,又排除了概率极低的离谱选项。

Top-k 更粗暴:只从概率最高的 k 个 token 里选。

实际使用中通常组合多种策略,比如 Top-p = 0.9 加 Temperature = 0.7。不同场景调不同参数——写代码时温度低一点求准确,写故事时温度高一点求发散。

自回归:一个字一个字地蹦

选出了一个 token(比如”今”),把它拼到输入序列的末尾,然后整个序列再走一遍上面的流程——嵌入、穿过所有 Transformer 层、输出头、采样——再选出下一个 token。如此循环往复。

这就是自回归生成:每一步都依赖前面所有已生成的内容,一个 token 一个 token 地往外蹦。

循环终止的条件有两个:生成了特殊的结束符(EOS),或者达到了预设的最大长度。模型生成”今天天气不错”这 5 个 token,实际跑了 5 次完整的前向传播。

这里有一个重要的工程优化叫 KV Cache。注意前面讲注意力时,每个 token 要算 K 和 V 向量。生成新 token 时,前面所有 token 的 K 和 V 跟上一轮算出来的一模一样,没必要重算。模型会把它们缓存起来,每轮只算新 token 的部分,再跟缓存拼在一起做注意力。这也是为什么大模型推理时显存占用会随生成长度不断增长——缓存越来越大。

有一个容易产生的误解需要澄清:模型没有”先想好整句话再说”这个过程。 它在每个时间步只看到前面已有的内容,预测下一个 token。看起来连贯通顺的回复,是上下文信息在每一步的概率分布中自然引导出了语义连贯的选择——不是规划的结果,而是统计规律的涌现。

7B、70B:参数量到底是什么

聊完推理流程,一个自然的问题是:常看到”7B 模型""70B 模型”,这些数字指什么?

参数就是模型里所有可学习的权重数字。 上面每一步涉及的矩阵,里面的每个数字都是一个参数。粗略算一下:

  • 嵌入矩阵 100000 × 4096:约 4 亿个参数
  • 每层注意力的 Q/K/V/输出投影矩阵:约 6700 万参数
  • 每层前馈网络的两个矩阵:约 1.3 亿参数
  • 单层合计约 2 亿参数

堆 96 层就接近 200 亿。加上嵌入和输出头,就到了某个 B 级别。B 是 billion(十亿),T 是 trillion(万亿):7B = 70 亿参数,70B = 700 亿参数。

决定参数量的本质上是三个旋钮:隐藏维度(向量有多宽)、层数(堆多少层)、注意力头数和前馈网络膨胀倍率(每层有多复杂)。三者一起放大,参数量就从 7B 涨到 70B。

参数量决定了模型的容量上限——能记住多少知识、能处理多复杂的推理。但大不等于好。同样 7B 的模型,用不同质量的训练数据、不同的对齐策略,效果可以差出一个量级。近年的趋势是让小模型吃更多高质量数据,比如 Llama 3 8B 的训练数据量远超同体量前辈,效果已经追上了早期 70B 模型。

参数量也直接决定推理成本:你每问一句话,就是让输入依次乘过所有这些参数矩阵。70B 比 7B 大 10 倍,计算量和显存占用也大约是 10 倍。

这套机制的代价和局限

理解了推理流程,几个常见现象就有了解释。

为什么第一个字出来慢,后面就快了? 第一个 token 的生成需要处理整段输入(可能有几千个 token),所有位置之间两两算注意力,计算量很大。这一步叫 prefill。之后每生成一个 token,有 KV Cache 兜底,只需算新 token 和历史 token 之间的注意力,增量计算量小得多。

为什么上下文长度有限制? 注意力的计算量随序列长度平方增长——1000 个 token 时算 100 万次点积,10000 个 token 就是 1 亿次。KV Cache 的显存占用也随长度线性增长。所以每个模型都有上下文窗口上限,超了要么截断要么报错。

为什么会”一本正经地胡说八道”? 模型在每一步只是做概率预测:给定前文,下一个 token 最可能是什么。它没有”我不确定”这个内部信号。如果训练数据里某个说法模式出现频率很高,模型就会很”自信”地沿着这个模式生成下去,哪怕内容是错的。这是幻觉(hallucination)的根源之一。