频率惩罚

Frequency Penalty

Frequency Penalty 频率惩罚是 OpenAI 兼容 API 的采样参数(-2.0 ~ +2.0),对每个已出现 Token 的惩罚量与其出现次数成正比,专门抑制说话结巴、同一段文字无限复制粘贴、代码中 print(1) 连打等现象。

详细解释

频率惩罚(Frequency Penalty)存在惩罚 Presence Penalty 是孪生兄弟,都在 Logits 层面对已出现过的 Token 减分。区别只在于衰减函数

  • Presence Penalty:出现过 1 次就减固定分(-p),出现 100 次还是减 -p
  • Frequency Penalty:出现 N 次就减 - f × N,出现越多减越多,线性累积

所以 Frequency Penalty 是专治”复读机”——模型一旦陷入”谢谢、谢谢、谢谢、谢谢”或者 <p>段落</p><p>段落</p><p>段落</p> 无限套娃,Frequency Penalty 会让第 4 个”谢谢”的分数比第 1 个低 4 倍,从而打断正反馈链条。

惩罚数值的数学形式(vLLM / OpenAI 兼容实现通用)

每生成新的一步 Token,对词表中每个候选词 i 执行:

logits[i] -= frequency_penalty * count[i]    # count[i] = 到目前为止这个词出现了几次
logits[i] -= presence_penalty  * (1 if count[i] > 0 else 0)

然后再走 Softmax → Sampling。

由此直接推出几个重要结论

  1. 只要出现过 1 次,Presence 和 Frequency 都生效;出现越多,Frequency 项越大。
  2. 如果 f=1.0、一个词出现了 3 次,相当于减 3 分。对 logits 尺度来说 3 分是非常大的量(logits 差 1 分对应概率差 2.7×),所以数值很小就很有效果
  3. 两者相加最大值建议不要超过 2.0,否则几乎所有词都被砍到没法正常说话。

常见场景推荐配置

场景frequency_penaltypresence_penalty其他配合
开放对话(闲聊/客服)0.1 – 0.30.2 – 0.6temperature=0.7–1.0
创意/营销文案0.2 – 0.50.8 – 1.2配合 Top-P=0.9
代码生成0.3 – 0.80.1 – 0.3Stop Sequence 防止输出额外注释
结构化 JSON/数据抽取0.0 – 0.10.0结构化输出 + temperature=0.2
RAG 问答/摘要0.1 – 0.30.2 – 0.5避免车轱辘话复述原文
多语言翻译0.00.0翻译不需要”新意”,不要开惩罚

唯元智创 默认 presence_penalty=0, frequency_penalty=0,你可以按上表按场景选择。控制台 Playground 也提供了这两个参数的滑块实时预览效果。

常见问题

模型输出到一半开始重复同一句话,我把 Frequency 调到 2.0 为什么反而更糟?
2.0 是上限,此时所有已经出现过的正常词(如”的、是、你、我”)都被巨量减分,模型反而只能选完全没出现过的生僻词或乱码。正确处理方式:先检查是不是 max_output_tokens 设太大导致模型”没话可说被迫复读”,然后 Frequency 回到 0.3–0.8 + Presence 0.2 组合即可;再不济加 Stop Sequence 比如检测到连续重复 3 次同样的 n-gram 就停止。
Presence=0 Frequency=0 就一定安全吗?
不是。大模型生成长文本有”吸引子效应”:采样是正反馈过程,同一个词出现一次之后条件概率就升高,越到后面越容易陷入局部短语循环。现代模型(GPT-4o、Claude 3.5)比 2023 年模型抗复读好很多,但在超长输出(长度 > 4K Token)、或者 Temperature 很低(小于 0.3)的情况下仍会偶然触发。建议只要输出长度在 1K Token 以上,就至少开 Frequency=0.2 以防万一。
为什么翻译任务反而要把两个惩罚都设 0?
因为目标语言里有些词(比如法律文本中的”本合同”、“甲方”)本来就应该反复出现。如果开了惩罚,模型会为了避开重复词而换词——翻译术语的一致性就会被破坏。类似的道理:代码生成中如果用了同一个变量名,也要控制 Frequency 不要太高,否则模型会为了不重复而瞎改变量名。