图 1:本文封面,复读循环的三重成因与对策方向。
先说明属性:这是一篇经典题解析,题目出自本地历史面试资料整理,与任何公司的当前招聘无关。「大模型为什么会出现复读机问题」是文本生成方向的常考题,很多人的答案停在「解码策略不够好」这一句,但真正要讲清的是三层成因——解码、数据、概率自我强化——以及每层对策能管到哪、管不到哪。
一句话结论:复读不是偶发 bug,而是概率分布在特定条件下的稳态。
先定义现象:生成重复可以发生在三个粒度——字符或词级别(同一个词反复刷屏)、语句级别(同一句话循环输出)、章节级别(换个提示词输出却千篇一律)。面试里通常聚焦前两种,即生成过程陷进了循环。
成因一:最大化式解码没有跳出机制。
贪心解码每一步都取概率最高的词,束搜索(beam search)保住的是总概率最高的几条路径,两者的共同点是「只看概率,不看历史里出现过什么」。2022 年发表的对比解码研究(arXiv:2202.06417)明确指出:这类最大化式解码经常产生「退化」输出——不自然、包含大量重复,并把深层原因之一归结为词元表示的各向异性,即不同词元的表示挤在向量空间的一小片区域里,模型对「该换个词」这件事缺乏区分度。换言之,解码器只是把模型给的分布原样执行,分布一旦偏向重复,贪心会一条道走到黑。
成因二:训练数据与训练目标埋下了种子。
训练语料里天然存在大量重复模式——模板化的商品标题、格式化的列表、反复的套话——模型在下一词预测的目标下会把「接着重复」也学成一个合法动作。2019 年发表的 unlikelihood training 研究(arXiv:1908.04319)给了一个更尖锐的观察:标准的似然目标本身就有问题,它让模型给「含重复的序列」和「高频词」分配了偏高的概率——复读倾向不是解码阶段才出现的,是训练目标亲手写进分布里的。
成因三:输出回填上下文,概率自我强化。
这是最容易被忽略、却最能解释「为什么会一直重复同一个词」的一层。自回归生成的每一步都把刚生成的词追加回上下文,再据此预测下一个词。2022 年关于归纳头(induction head)的机理解释研究(arXiv:2209.11895)表明,Transformer 里存在专门执行「前文出现过 [A][B],再见到 [A] 就倾向预测 [B]」的注意力头——这套机制本是上下文学习能力的来源,但在生成场景里它意味着:一旦某个片段在输出里出现了两次,模型就会越来越确信「下一个词还应该是它」。重复得越多,分布越尖,循环越死。
用一段玩具二元模型把三层机制串起来看(Node.js 22.15.0,node rep.js):转移表里逗号之后「今天」概率最高,纯贪心立刻陷入循环;给已出现过的词按出现次数施加重复惩罚后,第三次回到岔路口时惩罚把「今天」压到了备选词之下,输出拐向新句子:
纯贪心解码: 今天天气很好,今天天气很好,今天天气很好,今天天气很好,……
加重复惩罚1.5: 今天天气很好,今天天气很好,今天天气很好,我爱编程。
对策各管多少:解码层治标,数据层治本。
重复惩罚(repetition penalty)的思路出自 2019 年的 CTRL 论文(arXiv:1909.05858):把已出现词元的得分除以一个大于 1 的系数(Hugging Face 的生成参数文档里 repetition_penalty 默认 1.0 即不惩罚),出现过的词再次被选中就更难;更硬的办法是 no_repeat_ngram_size,直接禁止指定长度的 n-gram 第二次出现。代价是压过头会损伤流畅度——人名、术语本来就需要重复。采样类策略(temperature 拉平分布、top-p 截断长尾)靠引入随机性给非重复词留出口,但随机性同时牺牲可控性。对比解码则在选词时额外惩罚「与上文表示过于相似」的候选,直接针对成因一与三。训练层的 unlikelihood 目标和语料去重才是治本:把重复模式的来源从分布里削弱。工程上的真实答案是组合拳——线上通常是「数据去重 + 适度惩罚 + 采样参数调优」一起上。
图 2:自回归循环的成因链条与三层对策的分工。
面试现场的答题结构。
先给结论「复读是概率分布在解码、数据、自我强化三层因素下的稳态」,再逐层展开:最大化式解码没有跳出机制且词元表示各向异性;训练语料与似然目标给重复序列分配了偏高概率;输出回填上下文后归纳头机制让重复自我强化。然后按「解码层治标、数据层治本」给对策,并点明每种对策的代价——惩罚过大伤流畅度,采样过强伤可控性,对比解码多一层计算。答到「对策之间不是替代关系而是组合拳」这一层,就把问题讲透了。你在生成业务里调过重复惩罚吗?惩罚调大之后遇到过哪些副作用?欢迎聊聊。