训练代码生成模型实现自我调试输出
代码生成——将自然语言规范自动转换为计算机代码——是大语言模型(LLM)最具前景的应用之一。但编程任务越复杂,LLM出错的可能性就越高。当然,任务越复杂,人类编码者同样更容易出错,这正是调试成为软件开发流程关键组成部分的原因。
在2024年神经信息处理系统大会(NeurIPS)上发表的一篇论文中,描述了一种训练LLM成为更优秀调试器的新方法,同时提升代码生成能力。此前利用LLM调试代码的尝试主要使用少样本学习,即提供少量成功调试示例由LLM推断其余部分。相比之下,该研究同时使用监督微调(SFT)和强化学习(RL)来专门训练LLM进行调试。由于调试训练数据稀缺,研究利用LLM生成高质量合成训练数据。
数据合成
目前存在多个广泛使用的公共数据集用于训练代码生成模型,这些数据集包含自然语言提示、代码中的规范实现以及单元测试(可用于测试生成代码全部功能范围的特定输入序列)。但用于调试模型的训练数据相对稀少。
为创建调试数据集,研究从现有代码生成数据集中选取多个样本。将每个自然语言提示反复输入代码生成模型,为同一提示生成多个不同输出(例如20个)。随后对这些输出运行相关单元测试,仅保留未通过测试的样本——即存在缺陷的代码。
接下来,将有缺陷的代码与单元测试生成的错误消息一起输入LLM,提示LLM解释错误发生的位置和原因。最后,将LLM的诊断结果、缺陷代码和错误消息重新输入LLM,并附上修复缺陷的指令。这是链式推理的一种形式:先前研究表明,要求LLM在执行操作前解释其意图往往能提升性能。
随后对修订后的代码执行单元测试,此次仅保留通过所有测试的修订版本。至此获得包含以下元素的新数据集:自然语言提示、这些提示的缺陷实现、错误诊断、调试后的代码以及单元测试。
模型更新
获得该数据集后,通过SFT和RL两种方式更新调试模型。在两种更新方法中,实验了要求链式推理解释后再请求代码修订的训练方案,以及直接要求修订的方案。
使用SFT时,向模型输入自然语言指令、缺陷代码和单元测试的错误消息。根据单元测试性能评估模型输出。
使用RL时,模型与训练数据进行迭代交互,尝试学习能最大化奖励函数的策略。经典RL学习算法需要连续奖励函数以探索优化空间。单元测试反馈是二元的,因此是离散的。为克服此限制,除了单元测试成功率,RL奖励函数还包含修订代码的CodeBLEU分数(衡量其与规范示例代码的差异),提供连续奖励信号。
单元测试应用耗时且资源密集,因此基于CodeBLEU分数训练也开启了直接使用规范示例训练的可能性,这是一个计算效率更高的过程。实验表明该方法确实能提升调试性能——尽管不如结合单元测试结果的训练效果显著。
评估
实验使用三种模型类型:完全依赖提示工程的原始LLM;仅使用SFT更新数据集的LLM;同时使用SFT和RL更新数据集的LLM。使用三种不同LLM架构实现每种模型类型,对每类模型测量三组输出:初始生成、直接修订初始生成、涉及链式推理的修订。此外还研究两种生成范式:一种给予模型一次生成正确代码的机会;另一种给予10次机会。共进行24组不同比较。
整体而言,更新后的模型表现优于提示工程基线。在除一种情况外的所有案例中,同时通过SFT和RL更新的模型版本表现优于仅通过SFT更新的版本。总体而言,研究展示了利用执行反馈和规范示例来更好调试代码模型并提升其生成性能的可扩展方法。
关键技术指标:在StarCoder-15B、CodeLlama-7B和CodeLlama-13B等代码LLM的实验中,该方法在MBPP等标准基准数据集上将pass@k分数提升达39%。pass@k指标要求模型生成k个自然语言规范的实现,只要至少一个实现通过预设测试即视为成功。
创新点:
- 首创结合监督微调与强化学习的LLM调试训练框架
- 开发基于LLM的合成数据生成管道(缺陷代码→错误诊断→修复代码)
- 引入CodeBLEU连续奖励机制解决单元测试二元反馈限制
- 证实链式推理在代码调试任务中的有效性
该方法为自动化编程辅助系统提供了新范式,通过自我调试机制显著降低代码生成错误率,为软件开发自动化带来重要进展。