vLLM-Ascend 异步调度下 Repetition Penalty 失效问题排查与修复
作者:昇腾实战派
知识地图:https://blog.csdn.net/Lumos_Lovegood/article/details/161601003
背景概述
在将 MuSe-0.6B 模型从 GPU 迁移至 Atlas 800I A2 平台时,发现模型在推理过程中出现重复输出问题。该模型基于 Qwen3-0.6B 微调,采用 decoder-only causal LM 架构,自回归生成离散音频 token。经过逐层排查,最终定位问题根因在于在异步调度场景下 repetition_penalty 未能作用于已生成 token。
模型结构
MuSe-0.6B 是基于 Qwen3-0.6B 微调的语言模型,使用 MuCodec 进行离散音频 tokenization。模型为 decoder-only causal LM,自回归生成离散 token,词表中包含音频 token(如 <AUDIO_xxx>)。其 config.json 关键参数如下:
architectures = ["Qwen3ForCausalLM"]
model_type = "qwen3"
dtype = "bfloat16"
num_hidden_layers = 28
hidden_size = 1024
intermediate_size = 3072
num_attention_heads = 16
num_key_value_heads = 8
head_dim = 128
hidden_act = "silu"
rms_norm_eps = 1e-6
rope_theta = 1000000
max_position_embeddings = 40960
vocab_size = 168055
tie_word_embeddings = true
layer_types = 28 个 "full_attention"
sliding_window = null
use_sliding_window = false
问题定位过程
1. 现象复现
按照 MuSe-0.6B 官方推荐参数进行测试,GPU 上无重复输出,而 Atlas 800I A2 上出现明显重复输出。

2. 排除主模型计算问题
开启确定性计算,固定输入和采样参数,对 GPU 和 NPU 逐层打印关键 tensor 的 L1 norm,并对比 logits entropy。
主模型关键 tensor 大体对齐,entropy 也能对齐,初步判断主模型前向计算不是主要问题,排查重点转向 logits 后处理和 sampler 状态。

进一步分析发现,首 token 的 logits 相差约 2%。hidden_states 整体相对误差较小(约 0.0007),但单独取最后一个 token 的 hidden_states 比较时,误差达到 1.37%。逐层打点显示,MLP 部分误差略有增长(1%~3%),但 attention 部分会回落。MLP 部分仅包含矩阵乘和 silu 激活函数,并非容易产生计算误差的环节,因此该误差被视为正常范围。

结论:主模型计算在 NPU 上能够对齐 GPU。
3. 聚焦后处理
确认主模型计算无误后,将排查重点转向采样后处理环节。后处理主要包括:
- temperature
- top-p / top-k
- logits processors
- repetition / frequency / presence penalty
当 temperature=0 时,GPU 也稳定出现重复输出,说明模型本身在 greedy 解码下存在重复倾向。MuSe-0.6B 为音乐模型,音乐中允许重复旋律,因此模型本身有概率重复输出。加入 repetition_penalty=1.3 后,GPU 不再重复,但 NPU 仍然重复。
下图展示了确定性计算下,temperature=0、repetition_penalty=1.3 时 GPU 和 NPU 的输出对比:

通过打印后处理前后候选 token 的概率值及对应 logits 变化,确认在 Atlas 800I A2 上 penalty 确实未生效。

4. 定位 output_token_ids
进一步打印 sampling metadata,发现:
- GPU 上传给 penalty 的
output_token_ids是真实已生成的 token id。 - NPU 上传给 penalty 的
output_token_ids保留为-1。
NPU 上:

GPU 上:

-1 是异步调度下的占位符。正常情况下,下一轮 sample 前应回填为真实 sampled token id。如果始终为 -1,repetition penalty 无法统计已生成 token,自然不会对重复 token 降权。
根因分析
vLLM-Ascend 0.18.0 的 NPUModelRunner 在异步调度场景下,缺少与 GPUModelRunner 对齐的 output token 回填逻辑。
正确链路应为:
- 当前 step 采样后,异步调度先将 sampled token 以
-1占位写入request.output_token_ids。 - 异步 output 创建后,通过
set_async_sampled_token_ids(...)保存真实 sampled token 的 CPU copy。 - 下一次 sample 前,通过
update_async_output_token_ids()将sampling_metadata.output_token_ids中的-1替换为真实 token id。 - repetition penalty 使用真实
output_token_ids统计已生成 token,并对这些 token 的 logits 降权。
问题版本中,NPU 路径未完整执行第 2 步和第 3 步,导致 output_token_ids 长期保留 -1。因此 apply_repetition_penalties 无法获取 generated token,repetition_penalty=1.3 实际未生效,最终表现为 NPU 重复输出。
官方修复
该问题在 vLLM-Ascend 后续版本中已修复:
commit: 69c4499180c63ae578b6728a47545082c843e811
title: [BugFix] fix bug that repetition_penalty is not effective in asynchronous scheduling
link: https://github.com/vllm-project/vllm-ascend/commit/69c4499180c63ae578b6728a47545082c843e811
修复点:
- NPU async output 创建后调用
self.input_batch.set_async_sampled_token_ids(...),保存真实 sampled token。 NPUModelRunner._sample()调用 sampler 前执行self.input_batch.update_async_output_token_ids(),将-1占位 token 回填为真实 token id。- 增加单测验证 sampler 被调用前
output_token_ids已完成更新。
官方说明明确指出:GPUModelRunner 会在每次 sample 前更新 sampling_metadata.output_token_ids,但 NPUModelRunner 遗漏了这一步,导致 apply_repetition_penalties 无法获取 generated token,进而使 repetition_penalty > 1 不生效。
鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。
更多推荐

所有评论(0)