GiFT: Gibbs Fine-Tuning for Code Generation¶
会议: ACL 2025
arXiv: 2502.11466
代码: https://github.com/Alex-HaochenLi/GiFT
领域: 文本生成
关键词: 代码生成, 自训练, Gibbs采样, 合成数据, 边际分布
一句话总结¶
提出 Gibbs Fine-Tuning(GiFT),受 Gibbs 采样启发,通过"代码→描述→代码"的迭代翻译从边际分布而非条件分布中采样自生成代码,结合困惑度引导的长尾数据选择,在 APPS+/MBPP+/CodeInsight 上比标准自训练提升最高 9.8%。
研究背景与动机¶
- 领域现状:用合成数据训练 LLM 进行代码生成已成为主流。自训练方法(如 STaR、RFT)让模型生成多个代码候选,保留通过测试的正确代码用于微调,迭代执行直到收益消失。
- 现有痛点:自训练中所有代码从条件分布 \(P(c|d_i)\) 中采样——固定描述 \(d_i\) 生成代码。但同一"意图"可以用多种方式描述,不同描述会激活不同的代码路径。从单一描述条件采样会过度代表某些代码、欠代表另一些。
- 核心矛盾:条件分布 \(P(c|d_i)\) ≠ 边际分布 \(P(c)\)。前者受限于特定描述的措辞偏好,后者才能覆盖"意图"空间中所有有效代码。
- 本文要解决什么? 如何从边际分布采样代码,同时处理自生成代码的长尾不平衡问题。
- 切入角度:Gibbs 采样是经典 MCMC 方法——通过交替从各维度的条件分布采样来逼近联合分布。类比到代码生成:交替执行"从描述生成代码"和"从代码生成描述"即可逼近描述-代码的联合分布。
- 核心idea一句话:用 Gibbs 采样式的描述↔代码迭代翻译,让自生成代码从边际分布采样。
方法详解¶
整体框架¶
对种子数据集中每个描述 \(d_i\):(1) 生成代码 \(c_1 \sim P(c|d_i)\);(2) 从代码反向生成新描述 \(d_1 \sim P(d|c_1)\);(3) 从新描述再生成代码 \(c_2 \sim P(c|d_1)\);(4) 重复若干迭代。收集所有中间代码,用困惑度加权采样选择训练数据,与原始 \(d_i\) 配对进行微调。
关键设计¶
- Gibbs 式迭代翻译:
- 做什么:从描述-代码联合分布的边际分布中采样代码
- 核心思路:模拟 Gibbs 采样——交替从 \(P(c|d)\) 和 \(P(d|c)\) 采样。第 \(k\) 步:\(c_k \sim P_\mathcal{M}(c|d_{k-1})\),\(d_k \sim P_\mathcal{M}(d|c_k)\)。多步后 \(c_k\) 逼近边际分布 \(P(c)\)
-
设计动机:理论分析表明,用边际分布的代码来微调等价于对条件分布损失取关于所有等价描述的期望 \(\mathcal{L}_{marg} = \mathbb{E}_{d' \sim P_d}[\mathcal{L}(d')]\),隐式降低了因特定描述措辞引入的偏差
-
困惑度引导的长尾数据选择:
- 做什么:在自生成代码中平衡头部(常见)和尾部(罕见)代码的比例
- 核心思路:计算每个自生成代码的困惑度(PPL)。高 PPL 代码对应分布的尾部——罕见但有价值。用加权随机采样选择训练数据,PPL 越高被选中概率越大
-
设计动机:不平衡的自生成数据会导致模型坍缩——头部代码被过度学习,尾部代码被忽略。困惑度加权恢复长尾多样性
-
理论保证:
- 证明了边际分布微调的损失 \(\mathcal{L}_{marg}\) 等于条件分布损失 \(\mathcal{L}(d_i)\) 加上一个"去偏"项
- 该去偏项鼓励模型学习在不同描述下都一致的代码模式
损失函数 / 训练策略¶
- 标准 SFT 损失,仅在代码 token 上计算
- 中间生成的描述不用于训练——仅作为 Gibbs 采样的桥梁
- 迭代自训练:每轮用更新后的模型生成新代码
实验关键数据¶
主实验¶
| 模型 | 方法 | MBPP+ | CodeInsight | APPS+(Intro) | APPS+(Interview) |
|---|---|---|---|---|---|
| DeepSeek-Coder-6.7B | 基线(条件) | 66.7 | 36.5 | 44.4 | 15.3 |
| DeepSeek-Coder-6.7B | GiFT(边际) | 67.9 | 38.8 | 54.2 | 16.0 |
| CodeLlama-7B | 基线(条件) | 55.3 | 28.5 | 34.7 | 8.3 |
| CodeLlama-7B | GiFT(边际) | 56.0 | 30.5 | 38.9 | 9.2 |
消融实验¶
| 配置 | 效果 | 说明 |
|---|---|---|
| 条件采样 vs 边际采样 | 边际 +1.2~9.8% | 核心验证——边际分布采样确实更好 |
| w/o 困惑度选择 | 性能下降 | 尤其在迭代多轮后差距明显 |
| 1步 Gibbs vs 多步 Gibbs | 多步更好 | 更多迭代更接近真正的边际分布 |
| 还可用于蒸馏场景 | 有效 | GiFT 与蒸馏正交互补 |
关键发现¶
- 在困难基准(APPS+ Interview)上改进最大(+9.8%)——边际采样增加的代码多样性在难题上价值更大
- 困惑度选择在多轮迭代中防止模型坍缩——没有它,迭代自训练3轮后性能开始下降
- 中间生成的描述也可用于训练(作为数据增强),但增益相对较小
- GiFT 可与蒸馏方法结合——用 GiFT 生成的边际代码替换直接条件生成的代码,蒸馏效果更好
亮点与洞察¶
- Gibbs 采样的类比精准且优雅——将描述←→代码的交替翻译对应到 Gibbs 采样的交替条件采样,有坚实的理论动机。
- "不同描述激活不同代码路径"是被忽视的重要洞察——同一意图的不同措辞(如"搜索字符串中的正则匹配" vs "在文本中找到正则表达式模式")会产生语义等价但实现不同的代码。
- 困惑度作为长尾检测器简单有效——高 PPL 的代码更可能是罕见但有效的实现方式,值得系统学习。
- 方法通用不限于代码——可迁移到数学推理(问题↔解答迭代翻译)、翻译(源语↔目标语迭代)等其他自训练场景。
- 理论分析严谨——证明了边际分布微调等价于对条件分布损失取关于所有等价描述的期望,降低了因特定措辞引入的偏差。
局限性 / 可改进方向¶
- 需要从代码反向生成描述(code summarization),摘要质量取决于模型的总结能力,低质量摘要可能破坏 Gibbs 链
- Gibbs 迭代增加了数据生成的计算成本——每步都需要一次生成+一次摘要
- 仅在 6.7B/7B 模型上验证,更大模型(如 33B/70B)的条件偏差可能更小,GiFT 的边际收益可能递减
- 测试用例的质量对正确代码的筛选至关重要——低质量或不完整的测试用例会引入噪声
- 多步 Gibbs 的收敛性没有理论保证——实证发现 3 步效果好,但何时收敛取决于任务
相关工作与启发¶
- vs RFT/STaR: 标准自训练从 \(P(c|d)\) 采样,GiFT 从 \(P(c)\) 采样,覆盖更广
- vs InverseCoder: InverseCoder 用代码反向生成描述做数据增强,GiFT 的迭代翻译比单次反向更接近联合分布
- vs MathGenie: MathGenie 在数学领域做类似的"解答→题目"反向翻译,GiFT 在代码领域系统化了这一思路并添加了理论分析
评分¶
- 新颖性: ⭐⭐⭐⭐ Gibbs 采样类比新颖,理论分析扎实
- 实验充分度: ⭐⭐⭐⭐ 两个模型四个数据集,消融充分,但模型规模有限
- 写作质量: ⭐⭐⭐⭐⭐ 动机清晰,理论推导严谨,图示直观
- 价值: ⭐⭐⭐⭐ 自训练方法的重要改进,适用于多个领域