神经文本退化:为什么最大概率并不等于最好
用语言模型生成文本时,一个反直觉的现象长期困扰研究者:以最大似然为目标训练出的模型,如果直接用最大概率 token 逐个拼出句子,产出的文本往往平淡、重复,甚至陷入“我我我”式的死循环。2019 年,Holtzman 等人在论文《The Curious Case of Neural Text Degeneration》中系统描述了这一现象,并称之为神经文本退化(neural text degeneration)。他们发现,人类文本与机器文本在概率分布上存在系统性差异:人类更倾向于在少数几个高概率词之间做选择,同时偶尔会跳出高概率区域,使用低概率但语义上合理的词;而最大似然解码则把概率最高的词当作唯一选择,导致输出过于保守。
退化现象直接否定了“概率越高越好”的直觉。如果只取最大概率,模型会重复高频词,因为高频词在每一步都占据主导地位,而人类写作恰恰会避免这种机械重复。更关键的是,退化并非模型能力不足——同一个模型,换一种解码策略,生成质量可以显著不同。这意味着解码策略本身是一个独立的工程变量,值得单独设计和调优。
从贪心到采样:候选集截断的两种思路
要打破贪心解码的确定性,最直接的办法是引入随机性:不选最大概率,而是按概率分布随机抽样。但直接对全词表采样会带来另一个问题——词表尾部大量概率极低的 token(比如拼写错误、生僻字、无意义符号)会被频繁选中,破坏流畅性。于是工程上出现了“截断采样”:先砍掉一部分低概率 token,再在剩余集合中按概率抽样。
Top-k 采样是最早普及的截断方法:每一步保留概率最高的 K 个 token,把其余 token 的概率置零,再对保留集合重新归一化后抽样。K 是一个固定整数,比如 40 或 50。Top-k 的优点是简单、可控,但它的缺陷在概率分布形态变化时立刻暴露。
假设某个上下文中,前 3 个 token 的概率分别是 0.4、0.3、0.2,第 4 个是 0.05,其余都是 0.01 以下。此时 K=3 能保留大部分概率质量,截断是合理的。但在另一个上下文中,如果概率分布非常平坦,前 20 个 token 的概率都在 0.03 左右,K=3 就会丢掉大量合理候选,导致生成过于局限;反过来,如果分布非常尖锐,前 3 个 token 已经占了 0.95 的概率,K=3 又会把一些概率极低、几乎不会被抽到的 token 也纳入候选集,等于变相放大了尾部噪声。
Top-k 的固定截断无法感知分布形态:它不知道当前分布是尖锐还是平坦,也不知道该保留多少 token。这正是核采样要解决的问题。
核采样:按累积概率动态圈定候选集
核采样(Nucleus Sampling,即 Top-p)不再固定候选数量,而是根据概率分布本身动态决定截断点。具体做法是:先把所有 token 按概率从高到低排序,然后从最高概率开始逐个累加,直到累积概率达到预设阈值 p(通常取 0.9~0.95)。这个被选中的最小 token 集合就是“核”(nucleus),集合内的 token 总概率至少为 p。之后把核内 token 的概率重新归一化,再按归一化后的分布抽样。
用资料中的例子说明:假设当前分布为 A=0.4、B=0.3、C=0.2、D=0.05、
核采样的关键特性是候选集大小随分布形态变化:分布尖锐时,少量高概率 token 就能凑够 p,核很小,生成更保守;分布平坦时,需要更多 token 才能凑够 p,核变大,多样性更高。这种自适应特性正是它优于 Top-k 的根本原因。
论文作者指出,核采样通过截断概率分布中“不可靠的尾部”,在保留多样性的同时避免了低概率 token 带来的不流畅。实验表明,在困惑度(perplexity)和人工评估上,核采样生成的文本比 Top-k 和贪心解码更接近人类文本。
温度参数:在核采样之前重塑分布
温度(temperature)是另一个常用的采样参数,它不直接截断候选集,而是先对概率分布做变换,再交给采样策略处理。温度的作用公式为:
其中 T 是温度值。当 T=1 时,分布不变;T<1 时,概率取幂后差距被放大,高概率 token 更突出,分布更尖锐;T>1 时,概率差距被缩小,分布更平坦,低概率 token 被选中的机会增加。
温度与核采样是串联关系:先对 logits 除以温度,再计算 softmax 得到新分布,最后执行核采样。这意味着温度改变了分布形态,而核采样根据新分布动态决定截断点。两者相互作用会产生非直觉的结果:
- 当 T 很低(比如 0.5)时,分布变得尖锐,核可能只包含一两个 token,此时即使 p 设得较大,实际候选集也很小,生成接近贪心。
- 当 T 很高(比如 1.5)时,分布变得平坦,核会包含更多 token,多样性增加,但同时也可能把更多低质量 token 纳入候选。
因此,温度与 p 是联合调节的。低温度配合高 p,可以在保持一定多样性的同时提高确定性;高温度配合低 p,则可以在扩大探索范围的同时限制尾部噪声。实际调参时,通常先固定一个合理的 p(如 0.9),再根据任务需求调整温度。
场景对比:对话生成与故事创作的不同参数取向
核采样的参数设置需要结合具体场景。我们以两个典型场景贯穿全文:企业客服对话生成和开放式故事创作。
企业客服对话:目标是准确、一致、符合业务规范。用户问“如何退款”,模型应当给出标准流程,而不是发挥创意。此时需要低温度(T=0.7 左右)来压缩分布,同时 p 可以设得稍高(0.9~0.95),因为低温度已经让核变小,高 p 不会引入太多噪声。这样既能避免贪心解码的重复,又能保证回答稳定。如果 p 设得太低(如 0.8),在尖锐分布下核可能只剩一个 token,退化为贪心,容易产生生硬回复。
故事创作:目标是新颖、连贯、有想象力。模型需要跳出高频词,探索更多可能性。此时应使用较高温度(T=0.9~1.2)来平坦化分布,同时 p 设在 0.9 左右。高温度扩大了候选集,但低 p 会截断尾部,防止出现无意义 token。如果温度过高且 p 也高,生成会变得散乱,甚至出现语法错误。
下表总结了不同场景下的参数取向和权衡:
| 场景 | 温度 T | Top-p 阈值 | 候选集特征 | 主要风险 |
|---|---|---|---|---|
| 客服对话 | 0.6~0.8 | 0.9~0.95 | 核小,集中在高概率 token | 温度过低导致重复,p 过低导致单一 |
| 故事创作 | 0.9~1.2 | 0.85~0.95 | 核中等,包含更多中概率 token | 温度过高导致不连贯,p 过高导致噪声 |
| 代码生成 | 0.2~0.4 | 0.95~1.0 | 核极小,几乎贪心 | 温度稍高就会引入语法错误 |
| 摘要生成 | 0.8~1.0 | 0.9~0.95 | 核中等,平衡忠实与简洁 | p 过低导致摘要不完整 |
表格中的数值范围来自工程经验,并非论文结论。实际调参时应结合验证集评估。
核采样的失效模式:低熵分布下的困境
核采样并非万能。当概率分布非常尖锐(低熵)时,核采样会面临一个尴尬局面:无论 p 设多大,核都只包含极少数 token,因为累积概率很快达到阈值。例如,在代码生成或数学推理中,下一个 token 往往高度确定,分布可能呈现“0.9、0.05、0.02……”的形态。此时 p=0.9 的核只包含第一个 token,核采样退化为贪心解码。
这种退化并非坏事——在确定性任务中,贪心反而是优点。但问题在于,如果任务需要偶尔的多样性(比如代码注释生成),低熵分布会让核采样失去作用。更糟糕的是,低熵分布下温度参数也几乎无效:即使 T 很高,分布依然尖锐,因为概率差距太大,取幂后仍然悬殊。
另一个失效模式是 p 值设置不当导致的“截断过度”。如果 p 设得太低(如 0.5),在平坦分布下核可能只包含前几个 token,但这些 token 的概率都很接近,截断会丢失大量合理候选,导致生成质量下降。相反,p 设得太高(如 0.99),核可能包含数百个 token,尾部噪声被放大,生成变得不流畅。
此外,核采样对分布排序的依赖也带来实现细节问题。实际实现中,需要先对 logits 排序,再计算累积概率,这增加了计算开销。虽然现代推理框架已经优化,但在低延迟场景下仍需注意。
工程实现与可观测性
在工程上,核采样的实现通常位于解码循环中。以下伪代码展示了核心逻辑(不依赖特定框架):
def nucleus_sample(logits, p, temperature=1.0):
# 1. 应用温度
logits = logits / temperature
# 2. 计算概率
probs = softmax(logits)
# 3. 排序
sorted_probs, sorted_indices = sort(probs, descending=True)
# 4. 累积概率
cumsum = cumsum(sorted_probs)
# 5. 找到超过 p 的截断点
cutoff = find_first(cumsum >= p)
# 6. 保留核内 token,其余置 -inf
mask = zeros_like(logits)
mask[sorted_indices[:cutoff+1]] = 1
masked_logits = logits + (1 - mask) * (-inf)
# 7. 重新归一化并采样
final_probs = softmax(masked_logits)
return sample(final_probs)
关键状态变化:温度调整改变了 logits 的尺度,softmax 后概率分布随之变化;排序和累积概率计算确定了核的边界;置 -inf 操作相当于把核外 token 的概率清零,再 softmax 实现归一化。边界条件包括:当 p=1.0 时,核包含所有 token,退化为全词表采样;当 p 接近 0 时,核可能只包含最高概率 token,退化为贪心。
生产环境中需要观察的信号包括:
- 核大小(nucleus size):平均每步保留的 token 数量。如果核大小长期为 1,说明分布过于尖锐或温度过低,生成可能缺乏多样性。
- 重复率:连续 n-gram 重复的比例。核采样虽然缓解了退化,但高温度下仍可能出现重复。
- 困惑度:生成文本的平均负对数似然。困惑度低不一定代表质量高,但异常升高可能意味着核采样引入了过多噪声。
- 采样分布偏移:对比训练时的分布,如果核内 token 的分布与训练分布差异大,说明温度或 p 设置不当。
定位问题时,可以先固定温度,扫描 p 值,观察核大小和重复率的变化;再固定 p,调整温度,观察困惑度和人工评估分数。这种分步调参比同时调整两个参数更容易定位问题。
与替代方案的比较
核采样并非唯一的选择。与 Top-k 相比,核采样的优势在于自适应截断,但代价是实现更复杂,且需要排序操作。与温度采样(不截断)相比,核采样能有效抑制尾部噪声,但可能过度限制多样性。与贪心解码相比,核采样牺牲了确定性,换来了多样性。
实际系统中,常见做法是组合使用:先温度调整,再 Top-k 粗筛,再 Top-p 细筛。这种组合可以同时利用 Top-k 的计算效率(先取前 K 个 token 缩小排序范围)和 Top-p 的自适应能力。但组合也带来更多参数,调优成本上升。
核采样的主要局限在于:它假设概率分布能反映真实质量,但模型校准不良时,高概率 token 不一定更好,低概率 token 也不一定更差。此时核采样可能截断掉真正合适的 token。解决思路是引入惩罚或奖励机制,但这超出了核采样本身的范畴。
总结与未解决问题
核采样通过动态截断候选集,在质量与多样性之间找到了一个实用平衡点。它解决了 Top-k 固定截断在分布变化时的失效问题,也避免了全词表采样的噪声。但它的有效性依赖于一个前提:概率分布能够区分好 token 和坏 token。当模型校准不佳或分布极端尖锐时,核采样的优势会减弱。
尚未解决的问题包括:如何自动选择 p 值?目前主要依赖人工调参或网格搜索,缺乏理论指导。温度与 p 的联合优化也缺乏系统性方法。此外,核采样对长文本生成中的累积误差如何处理,仍是一个开放问题。
对于工程师而言,理解核采样的机制和失效边界,比记住一组推荐参数更有价值。因为参数需要随模型、任务和分布变化而调整,而机制理解能帮助你在新场景中快速找到合理的起点。
附录:核采样流程示意
以下流程图展示了核采样在解码循环中的位置和关键步骤,以客服对话生成中“用户询问退款”为例:
flowchart TD
A[输入用户消息] --> B[模型前向计算 logits]
B --> C[应用温度 T 调整 logits]
C --> D[softmax 得到概率分布]
D --> E[按概率降序排序]
E --> F[计算累积概率]
F --> G{累积概率是否达到 p?}
G -- 否 --> H[加入下一个 token 到核]
H --> F
G -- 是 --> I[确定核边界,置零核外 token]
I --> J[重新归一化核内概率]
J --> K[按归一化概率采样下一个 token]
K --> L{是否生成结束?}
L -- 否 --> B
L -- 是 --> M[输出完整回复]
图中关键转折点在于累积概率判断(G):当累积概率达到 p 时,核边界确定,后续 token 被截断。这个判断每步都执行,因此核大小随分布动态变化。在客服场景中,如果模型对“退款”后的下一个 token 高度确定(如“流程”),核可能很小,生成稳定;如果模型犹豫(如“方式”“政策”“申请”概率相近),核会变大,生成更有变化。