代码模型训练数据混合诊断 | AI生成和翻译

Home 2026.06

问题: 是否无法仅用代码训练代码模型——应该混合比如20%代码、80%其他内容?以及如何看待这种训练诊断?

回答:

这里有两个独立问题。让我依次回答。

1. “不能只用代码训练,应该是20%代码?”

可以用纯代码训练——CodeParrot、StarCoder-base、最初的Codex都是在几乎100%代码上训练的,效果不错。所以“必须混入自然语言”的说法不是硬性规则。但你直觉背后确实有真实的信号:

所以混合比例不是“20%代码”。通常是相反的——代码为主加上少量自然语言(10–40%)。自然语言有助于:注释、文档字符串、变量命名语义和推理。为什么它甚至对纯代码生成也有帮助?因为代码中充满了自然语言(标识符、注释、字符串),而自然语言预训练让模型对这些token有比仅在代码上下文中看到它们时更好的先验。

对于你的124M nanoGPT规模模型,混合比例不是你的问题。暂时不要添加自然语言。你的问题是数据质量,而诊断准确地指出了这一点。

2. 诊断——对吗?

基本正确,但有一个重要的修正。

正确的部分:

错误或误导的部分:

“空格token主导”的说法基本上不是问题。 26%的空格对于代码来说是正常的——缩进是真正的结构,不是噪声。GPT-2的BPE分词器在代码上表现很差,正是因为它将空白拆分成许多单个空格token;这是分词器的问题,不是数据问题。模型在def quicksort(arr):之后预测46.8%的空格并不是病态——换行加缩进确实是可能性最大的延续。因此我会忽略这一点。如果你想在这里找到一个真正的杠杆,可以切换到代码感知的分词器(StarCoder的分词器使用专用token处理连续空格/制表符),仅此一项就能改善有效上下文和损失。

“欠拟合而非过拟合”——训练损失 > 验证损失是一个值得检查的红旗,不能盲目相信。 对于从头训练的124M模型,训练损失高于验证损失通常意味着:

这并不自动意味着“模型无法学习模式”。124M可以学习github代码模式至约3.0。所以我会把问题3重新定义为“检查你的评估框架和dropout设置”,然后才下结论说容量/数据是瓶颈。

选项C(训练更长时间)被低估了,如果损失仍在下降的话。 在427k次迭代且单调递减的情况下,你并未达到计算饱和。但诊断正确地指出数据质量限制了你最终能降到多低——更干净的数据降低了渐近线,更多的步数只是让你沿着数据允许的渐近线前进。

我实际会做的顺序

  1. 将分词器换成StarCoder的(通过tiktoken/HuggingFace使用bigcode/starcoder分词器)。对代码来说性价比最高的改进;从根本上解决“空格”症状。
  2. 将数据换成bigcode/the-stack-dedup(或者过滤github-code,但更换比编写清理脚本工作量小)。选择3–5种语言,不要全部——语言越少,124M的信号越密集。
  3. 修复评估——确保在验证损失测量时dropout关闭,并且验证集划分是代表全体数据的随机洗牌,而非连续块。重新确认训练>验证的差距。
  4. 只有到那时才考虑更长的训练或混合自然语言。

诊断的直觉(在这个规模下数据比什么都重要)是正确的,且符合Karpathy的理念。我只会消除空格token的恐慌和忽略分词器的框架,并在相信“欠拟合”结论之前验证训练/验证反转。

如果你坚持使用github-code而不是更换,一个快速的清理过滤器:

def keep(text: str) -> bool:
    n = len(text)
    if n < 200:                      # 配置存根
        return False
    if text.count('<') / n > 0.05:   # HTML/javadoc过多
        return False
    head = text[:600].lower()
    if ('copyright' in head or 'license' in head) and 'def ' not in head and '{' not in head[:200]:
        return False                 # 许可证头部主导
    return True

但老实说——换成去重且经过筛选的数据集加上代码分词器,将胜过在原始转储上任何数量的正则清理。

参考文献:


Back Donate