垃圾模型输出故障排除 | AI生成和翻译

Home 2026.06

问题: 生成输出大部分是空白字符,偶尔出现 <div>};<* 这样的 token。给定一个在脏乱的 GitHub 代码上训练的模型,这是预期的吗?

回答:

是的——根据你诊断中的所有信息,这正是你可以预测的失败模式。但这里存在两种不同的问题,只有一种是“预期/良性的”。让我将它们区分开。

预期的情况(良性)

空白字符以及 };<div>*< 是你的语料库中频率最高的 token。 一个高困惑度(PPL ~32)的模型如果无法可靠地区分上下文,就会退化为无条件的一元分布——它只输出平均出现频率最高的内容,而忽略上下文。在 GitHub 代码中,这些就是:空格、换行、缩进、闭合括号、分号以及 HTML/javadoc 片段(<div> 直接来自你约 12% 的 HTML 污染)。因此,垃圾 token 的身份与“模型退回到先验分布”是一致的。这部分是预期的。

这与语言模型在训练不足时输出“the the the and of”的现象相同——它输出的是边际分布,而不是条件分布。

什么不是预期的——这才是真正的信号

看看你的张量形状:

x after transformer: (1, 3, 768) | logits: (1, 1, 50304)

两个危险信号:

1. 序列长度是 3。 你给模型输入了一个 3 token 的提示,transformer 的输出是 (1, 3, 768)。这本身没问题。但这意味着在生成过程中,你是在极短的上下文上操作的。如果你的生成循环没有将采样到的 token 追加回输入中(或者你的 KV cache / 上下文窗口坏了),模型就永远不会看到真正的前缀——它每一步几乎是在空上下文的基础上预测。在一个 1-3 token 的上下文中,即使是好的模型也会严重依赖先验分布。请验证你的自回归循环是否确实在增长上下文:

for _ in range(max_new):
    idx_cond = idx[:, -block_size:]          # 必须包含已生成的 token
    logits, _ = model(idx_cond)
    logits = logits[:, -1, :] / temperature  # 只取最后一个位置
    probs = F.softmax(logits, dim=-1)
    next_id = torch.multinomial(probs, 1)
    idx = torch.cat([idx, next_id], dim=1)   # <-- 追加,否则你会从头重新生成

如果缺少那个 torch.cat 或者 idx_cond 设置错误,你就会看到这种现象:无论模型训练得有多好,都会无休止地生成无结构的高频 token。

2. logits: (1, 1, 50304) —— 词表大小是 50304,而不是 50257。 这是 GPT-2 的词表填充到 64 的倍数(nanoGPT 为了效率使用 50304)。如果训练时也使用了相同的填充词表,那就没问题。但值得确认的是,你的分词器和模型的 vocab_size 在训练和推理之间是否完全匹配——不匹配会偏移每个 token ID,并精确地产生这种“有效的 token,但无连贯性”的输出。事实上,生成的 token 仍然是合理的代码 token(不是随机的 Unicode),这暗示映射关系可能是正确的,但还是确认一下。

如何判断你遇到的是哪个问题(一个测试)

在指责数据之前,用一个长的、符合分布的前缀进行提示,并贪婪采样temperature → 0,即 argmax):

prompt = "def quicksort(arr):\n    if len(arr) <= 1:\n        return arr\n    pivot = "
# 编码,输入完整提示,argmax 解码 50 个 token

总结

垃圾的内容(空白字符、大括号、<div>)是语料库中出现频率最高的 token,这对于高困惑度的模型来说是预期的。但“PPL 为 32 时大部分是空白”也是从接近空上下文中采样生成循环出错的经典症状,而你的 seq_len=3 让我想先排除这种情况——只需 5 分钟检查,且修复成本远低于重新 token 化 50GB 数据。

所以:先运行长前缀贪婪解码测试。如果还是垃圾,在碰数据之前,请检查 torch.cat 的追加操作以及训练/推理的 vocab_size 是否匹配。数据清洗仍然值得做——但不要让数据清洗掩盖一个可能的推理错误。


Back Donate