起因#
上一篇把 RNN 拆成了两层:state transition 和 output projection。State transition 维护 hidden state,output projection 从 hidden state 中映射出当前任务需要的预测结果。
继续看 MIT 6.S191 的 language model 部分时,下一个自然的问题是:对于语言模型,output layer 产出的东西到底长什么样?
直觉上觉得是"预测出一个词"。但其实不是。它是整个词表上的一个概率分布。
把这个分布写出来,就是条件概率:
$$P(x_{t+1} \mid x_1, \ldots, x_t)$$真的从头串一遍背后的概率论——conditional probability、joint probability、probability chain rule——会发现 next-token prediction 和 sequence modeling 之间有一个非常直接的数学连接。
这篇笔记把这条链路理一遍。
从 Hidden State 到概率分布#
上一篇已经建立了 RNN 的两层框架:
$$h_t = f(x_t, h_{t-1})$$$$\hat{y}_t = \text{task\_head}(h_t)$$对于语言模型,task head 通常是一个 linear layer 加 softmax:
$$z_t = W_y h_t + b$$$$p_t = \text{softmax}(z_t)$$\(z_t\) 叫做 logits,维度等于词表大小 \(|V|\)。softmax 把它变成一个合法的概率分布——所有分量非负,加起来等于 1。
flowchart LR
ht["h_t"] --> lmhead["LM Head(Linear)"]
lmhead --> logits["logits"]
logits --> sm["softmax"]
sm --> dist["P(next token)"]
\(p_t\) 不是"模型觉得下一个词是什么"。它是整个词表上的概率分布。每个词都对应一个概率值。
比如,在 “I love machine” 之后:
| token | 概率 |
|---|---|
| learning | 0.62 |
| translation | 0.08 |
| vision | 0.05 |
| … | … |
模型不是在说"下一个词一定是 learning"。它在说:在当前 context 下,learning 出现的概率是 0.62,translation 是 0.08,以此类推。
写成数学符号:
$$P(x_{t+1} \mid x_1, \ldots, x_t)$$竖线左边是"要预测的东西",右边是"已经看到的东西"。这就是 conditional probability(条件概率)。
条件概率#
先从最简单的情况说起。
\(P(A)\) 表示事件 A 发生的概率。比如 \(P(\text{下雨})\),可能是 0.2。
\(P(A \mid B)\) 表示在已知 B 发生的情况下,A 的概率。比如 \(P(\text{下雨} \mid \text{乌云})\),可能变成了 0.7。
关键在于:知道了额外的信息(乌云),概率发生了变化。条件概率描述的就是"获得额外信息之后,概率怎么变"。
回到语言模型。
$$P(\text{learning} \mid \text{I love machine})$$这不是在问 “learning” 这个词在英语里有多常见。它问的是:在已经看到 “I love machine” 的情况下,下一个 token 是 “learning” 的概率有多大。
context 不同,distribution 就不同。“I love machine” 之后,learning 的概率很高。“I love ice” 之后,cream 的概率很高,learning 几乎为零。
context 的作用,就是改变 next-token 的概率分布。
随机变量和观测值#
顺便澄清一个符号问题。
概率论里,大写 \(X_t\) 通常表示随机变量——“第 \(t\) 个位置上会出现什么 token"这件尚未确定的事。小写 \(x_t\) 表示具体的观测值——“第 \(t\) 个位置上的 token 实际是 machine”。
严格写法:
$$P(X_{t+1} = \text{learning} \mid X_1 = \text{I},\; X_2 = \text{love},\; X_3 = \text{machine})$$ML 文章里通常简写成:
$$P(x_{t+1} \mid x_1, \ldots, x_t)$$后面沿用这个简写。
整个 Sequence 的概率#
到目前为止讨论的都是单步的 conditional probability:给定前面的 token,下一个 token 的概率。
现在换一个更大的问题。一整句话:
$$\text{I love machine learning}$$出现的概率是什么?
写成:
$$P(x_1, x_2, x_3, x_4)$$这叫 joint probability(联合概率)——四个 token 同时以这个顺序出现的概率。
问题是:这个 joint probability 怎么算?
Probability Chain Rule#
概率论有一个恒等式,叫 chain rule(链式法则):
$$P(x_1, x_2, \ldots, x_T) = P(x_1) \cdot P(x_2 \mid x_1) \cdot P(x_3 \mid x_1, x_2) \cdots P(x_T \mid x_1, \ldots, x_{T-1})$$写成紧凑的连乘形式:
$$P(x_{1:T}) = \prod_{t=1}^{T} P(x_t \mid x_{用 “I love machine learning” 展开看:
$$P(\text{I, love, machine, learning})$$$$= P(\text{I})$$$$\times\; P(\text{love} \mid \text{I})$$$$\times\; P(\text{machine} \mid \text{I, love})$$$$\times\; P(\text{learning} \mid \text{I, love, machine})$$每一项都是一个 next-token conditional probability。
这个式子不是近似,是精确的恒等式。任何 sequence 的 joint probability 都可以这样拆。
如果模型能准确估计每一步的 \(P(x_t \mid x_{
这是 next-token prediction 最重要的数学基础。它不是一个和完整语言建模无关的小任务——probability chain rule 把两者直接连了起来。
语言中的 Token 不独立#
chain rule 对任何 joint distribution 都成立。但语言有一个性质让它特别有意义:token 之间不是独立的。
独立(independence)的意思是:
$$P(A \mid B) = P(A)$$知道 B 发生,不改变 A 的概率。
语言显然不满足这个条件。
\(P(\text{Francisco})\) 和 \(P(\text{Francisco} \mid \text{San})\) 差异非常大。“Francisco” 单独出现的概率不高,但在 “San” 之后,它几乎是唯一合理的选项。
同样,\(P(\text{learning})\) 和 \(P(\text{learning} \mid \text{machine})\) 也完全不同。
如果 token 之间真的独立,那 \(P(x_t \mid x_{ 正因为 token 不独立,context 才有意义。语言模型真正需要学的,不是每个 token 本身出现的频率,而是 context 如何改变 token 的 conditional probability。 回到 RNN。把 sequence 沿时间展开: 每一步的 hidden state 都可以用来预测下一个 token,每一步的预测都可以和真实 token 比较,产生一个 loss。 一段长度为 \(T\) 的 sequence,可以产生 \(T - 1\) 个训练目标: 如果只在 sequence 最后预测一次(比如序列分类),一整个 sequence 只给一个 supervision signal。Next-token prediction 则在几乎每个位置都给一个。 同样一段文本,next-token prediction 能从中提取的训练信号密度要高得多。 传统的 supervised learning 需要人工标注: 有多少标注数据,就有多少训练样本。标注成本限制了数据规模。 Next-token prediction 不需要人标注任何东西。 拿一段文本: 自动构造出训练数据: target 直接来自 raw text 本身。输入是前面的 token,target 就是紧跟着的下一个 token。不需要有人逐条标注"这里正确答案是 sat”。 这种训练方式叫 self-supervised learning(自监督学习)——supervision signal 从数据自身的结构中来,而不是从外部标注中来。 对语言模型来说,这意味着三件具体的事情。 一,不依赖人工 label。互联网上的文本可以直接拿来用。 二,每个 token position 都产生一个训练目标。一段 1000 个 token 的文本能构造 999 个 (input, target) pair。 三,原始文本的规模本身就非常大。书籍、网页、代码、论文——都是可以直接转化成训练数据的原料。 不需要标注,每个位置都是 target,原始数据量巨大。这是 next-token prediction 能够 scale 到大规模训练的基础。 “根据前面的 token 预测下一个 token”,听起来像一个很窄的任务。但不同的 context 对预测能力的要求差异很大。 要预测好这个,模型需要捕获 factual knowledge。 这里涉及 syntax——主语是 keys(复数),动词应该是 are 而不是 is。 这里涉及 entity tracking 和长距离依赖——模型需要记住 Bob 拿到的是 book。 为了在各种 context 下都能降低 prediction loss,模型会受到压力去捕获大量不同类型的结构:词法、句法、事实、推理、语义关联。 这里有一个有意思的地方。我们并没有告诉 hidden state 应该长成什么样。没有 label 说"这个 hidden state 应该编码语法信息"或者"这个 hidden state 应该记住实体关系"。模型只是为了做好 prediction,逐渐在 hidden state 里形成了对 history 有用的 internal representation。 这件事属于 representation learning 的范畴,可以单独写一篇。 最后补一步。 如果训练数据中真实的 next token 是 \(x_t\),训练时自然希望模型给出的条件概率越大越好:每个 Timestep 都是一个训练信号#
flowchart LR
x1["x₁"] --> h1["h₁"]
x2["x₂"] --> h2["h₂"]
x3["x₃"] --> h3["h₃"]
h1 --> h2 --> h3
h1 --> p1["predict x₂"]
h2 --> p2["predict x₃"]
h3 --> p3["predict x₄"]
p1 -.-> L1["loss₁"]
p2 -.-> L2["loss₂"]
p3 -.-> L3["loss₃"]
input target I love I love machine I love machine learning Self-Supervised Learning#
input label 一张猫的图片 cat 一封邮件 spam input target The cat The cat sat The cat sat on The cat sat on the The cat sat on the mat 为什么这么简单的 Objective 能学到很多东西#
连接到 Likelihood 和 Cross Entropy#
训练时通常取负号,变成最小化问题:
$$\mathcal{L} = -\sum_{t=1}^{T} \log P_\theta(x_t \mid x_{从 next-token prediction 出发,经过 chain rule,自然走到了这个训练目标。Cross entropy 的推导和信息论含义可以后面单独展开。
结论#
RNN 的 prediction head 产出的不是一个词,而是整个词表上的概率分布 \(P(x_{t+1} \mid x_{
Probability chain rule 把整个 sequence 的 joint probability 拆成一连串 next-token conditional probability。因此学 next-token prediction 和对 sequence distribution 建模是同一件事——这不是一个随意选择的小任务。
Next-token prediction 天然适合 self-supervised learning:不需要人工标注,几乎每个 token position 都产生训练目标,而原始文本的规模本身就非常大。一个 sequence 还能在多个 timestep 提供 supervision。这几件事叠在一起,是它能 scale 的基础。
参考资料#
- MIT 6.S191: Introduction to Deep Learning — https://introtodeeplearning.com/
- 重新理解 RNN:Hidden State、Output 和 Prediction 的区别
- 从 Forward 到 Optimizer:再看一遍 Training Loop