基于 DeepSeek GRPO 的 1.5B Rust 代码生成模型训练实战

编者按: 群组相对策略优化(GRPO)如何让小型专用模型在特定任务上实现性能提升?我们今天为大家带来的这篇文章展示了如何使用 GRPO,训练一个仅有 1.5B 参数的 Rust 代码生成模型,实现性能大幅提升。

文章详细介绍了作者基于 Qwen2.5-Coder-1.5B-Instruct 模型使用 GRPO 技术进行训练的实践经验。作者选择 Rust 语言作为实验对象,利用其严格的编译器和完善的工具链作为反馈机制,构建了一套基于格式验证与 Cargo 的奖励函数。通过单次 GRPO 训练,模型的代码编译通过率从 61% 提升至 80%,单元测试通过率从 22% 提升至 37%,成本不到 100 美元。

作者 | Greg Schoeninger

编译 | 岳扬

群组相对策略优化(Group Relative Policy Optimization,GRPO)已被证明是一种有效的算法,可用于训练大语言模型(LLMs),使其具备推理能力并在基准测试中持续提升性能表现。DeepSeek-R1 展示了如何通过监督式微调(Supervised Fine-Tuning)与 GRPO 技术的结合,引导模型达到与 OpenAI 的 o1 等顶尖模型相竞争的水平。

为了进一步探索其实践应用,我们尝试将这些技术应用于现实场景中。本文将概述如何利用 GRPO、自定义数据集和奖励函数来训练我们自己的定制化小型 LLM。下图是后续将展示的部分模型训练过程曲线预览。观察模型逐步学习生成代码块、优化可编译的有效代码,最终生成能通过单元测试的代码,这一过程颇具趣味性。

若希望直接上手实践,可访问该 GitHub 仓库[1]。

本文不会深入探讨 GRPO 的基础原理,若需了解其底层机制,请参阅个人开发者也能训练推理模型?GRPO 技术详解

01 为什么选择 Rust?

Rust 似乎是强化学习(RL)的绝佳舞台,因为可以使用 Rust 编译器和 Cargo 工具链。Rust 编译器不仅能够提供清晰的错误信息,还具备严格的类型检查。

在本项目中,我们首先想要验证的假设是:能否将 Cargo 作为反馈机制来教导模型成为更优秀的"程序员"。第二个实验旨在探索用 GRPO 训练,语言模型的最小可行规模是多少。这些实验特意限制在单节点 H100 上运行,既控制了成本又展示了实验的可实现性。

而且作为 Oxen.ai 的 Rust 开发团队,我们也正在探索一些有趣的跨界应用 🦀 x 🐂。

02 为何参数量选择 1.5B?

最近,有大量研究探索了小型语言模型在特定任务上的性能边界。当存在明确的反馈机制(如数学题的正确答案或程序的输出结果)时,模型规模可在保持高竞争力的同时大大缩小。

微软的 rStar-Math[2] 论文表明,在可验证的数学问题领域,模型可以进行推理。其 1.5B 参数量的模型性能超越了 GPT-4o 和 o1-preview。

我的假设是:在编码任务中我们同样能实现类似水平的性能,因为该领域同样存在类似的可验证奖励机制 ------ 代码能否通过编译?能否通过单元测试?

03 小型语言模型的优势

小型代码模型具备诸多优势,包括成本效益、处理效率、数据隐私性,以及针对自有代码库/编码规范进行定制的能力。此外,这本身也是项有趣的挑战。

我们的终极愿景是,让这类小型模型能完整执行所有 cursor 能够完成的任务:预测下一段代码、补全中间的代码内容,并在智能体的优化迭代循环中持续优化代码。不过,还是让我们先从最基础开始吧。

04 明确定义待解决问题

要让生成的代码能够通过单元测试,有多种不同的方法,我们最终尝试了几种方案。其中一种看似简单的方法是,提供一组可验证的单元测试,要求生成的代码必须通过这些测试。这将形成一套黄金标准的可验证答案集。

在尝试了该流程后,我们发现了两个主要问题。首先,若禁止模型在编写代码时查看单元测试,它就无法感知需要遵循的接口规范。在使用预构建的、已验证的单元测试进行评估时,许多错误最终表现为代码与单元测试之间的类型不匹配或命名不一致。

其次,若允许模型在编码时查看单元测试,就会牺牲开发者体验。除非您是一名严格的"测试驱动开发"实践者(Test Driven Developer),否则您可能更希望直接输入提示词,而非预先设计函数定义或单元测试。

我们没有试图想出更聪明的办法,而是最终选择了优化方案,使其更加简洁,将这个问题重构为要求模型在单次响应中同时生成代码和单元测试。

不过,单次生成方案存在模型"作弊"的风险:例如通过 println! 输出而非 assert 断言,生成可通过编译但无实际功能的代码。我们将在后续环节为此添加防护机制。

最终,我们通过一个详细的系统提示词(system prompt),为模型提供任务指引。

系统提示词(system prompt)以特定格式和风格为模型提供上下文,明确模型响应用户查询的预期形式。

05 数据集

在开始训练之前,我们需要一个数据集。最开始时,我们发现专门针对 Rust 语言的数据集较少,现有的大语言模型(LLM)基准测试大多聚焦于 Python。因此,我们首先将包含 Python 编程问题的提示词数据集转换为 Rust 语言版本。

我们从 Ace-Code-87k 数据集中随机选取了 20,000 个提示词样本,使用 Qwen 2.5 Coder 32B Instruct 模型生成对应的 Rust 代码及单元测试。随后通过编译器和测试框架运行这些代码,筛选出所有能通过单元测试的提示词、代码、单元测试三元组(prompt,code,unit_test triples),最终获得 16,500 组有效数据。该数据集被划分为 15,000 组训练数据、1,000 组测试数据和 500 组评估数据。

最终数据集[3]如下所示:

您可以通过以下模型运行记录跟踪整个流程:

1)将代码翻译为 Rust 语言:www.oxen.ai/ox/mbrp-pla...

2)编写 Rust 代码:www.oxen.ai/ox/mbrp-pla...

3)编写 Rust 单元测试:www.oxen.ai/ox/mbrp-pla...

有趣的是,在最终的 GRPO 训练方案中,我们移除了原始数据中的黄金标准 Rust 代码和单元测试列。在强化学习的不断迭代学习过程中,我们仅需将提示词作为输入即可完成训练,这为未来数据的收集提供了便利。尽管我们舍弃了用于训练的代码和单元测试数据,但知道仅需这些提示词就可以解决这个问题仍具有重要参考价值 ------ 这一特性将在后续章节详细解析。

06 设定 baseline

完成待解决问题的明确定义并构建完数据集后,我们需设定 baseline 以评估初始模型性能。我们将使用 Qwen/Qwen2.5-Coder-1.5B-Instruct 模型进行训练引导。

并统计以下指标:构建通过率(译者注:代码编译成功)、Clippy 检查通过率(译者注:通过 Rust 官方静态分析工具 Clippy 检测)、单元测试通过率。

由于 Clippy 检测依赖于代码的成功编译,这两项指标通常呈现强相关性。您可在 Oxen.ai 平台查阅原始数据[4],进一步分析具体案例。

07 与 SOTA 模型进行对比,孰强孰弱?

我们同时希望了解主流大型基础模型在该任务上的表现,以此为我们设定理论上的优化目标。经测试发现表现最佳的模型是 GPT4.5。

GPT4.5 的构建通过率高达 98%,他自己编写的单元测试的通过率达 87%,表现令人瞩目[5]。

Claude 3.7 Sonnet 以微弱差距位居次席。

至此,所有准备工作就绪 ------ 已完成明确定义待解决问题、数据集构建、baseline 设定及目标确立,终于可以进入激动人心的模型训练环节!

08 奖励函数设计

GRPO(群组相对策略优化)的精妙之处在于,其奖励机制可通过简单的 Python 函数实现。工程师只需定义奖励规则,模型便能自主优化策略。摩根士丹利的 Will Brown 将这种方法称为 Rubric Engineering,它为工程师提供了通过强化学习引导模型的便捷方法。

GRPO 奖励函数的输入参数包括 prompts(提示词)、responses(模型响应)及 target answers(目标答案)。实际上,部分评分标准甚至无需 target answers(目标答案),仅需基于生成内容本身的属性(如格式规范)进行评分。典型的评分维度包括:

  • 正确性(直接字符串匹配)
  • 响应长度(token 数量)
  • 响应格式(XML、JSON、代码等)
  • 外部工具调用(本例中为 Cargo 工具链)
  • 使用 LLM 进行评判(是否真实、是否有帮助、是否有危害等)

在本项目中,我们将结合格式验证与 Cargo 构建工具的执行结果构建奖励函数。初始代码基于 @willccbb 的优质代码实现:

gist.github.com/willccbb/46...

09 我们的"评分标准"

以下是我们针对该问题设计的"评分标准"。流程大致为:接收用户提示词 → LLM 生成代码及单元测试 → 通过多个评分函数评估模型响应。

奖励函数可以是一个简单的正则表达式,验证代码中是否包含有效的测试模块。

您会注意到,输入参数 prompts 和 completions 都是列表,输出的也是浮点数列表(list[float])。这是因为 GRPO 有一个名为 num_generations 的参数,用于控制每个 prompts 的生成次数。此外,在生成过程中可能会有多组 prompts + completions。GRPO 算法会为每个 prompts 生成 N 种不同响应,每个模型响应都会通过"评分标准"进行验证。下图展示了模型在尝试不同迭代方案时各评分项的通过情况:

随着时间的推移,模型将逐步掌握如何通过更多你定义的"评分标准"。借助 trl 库,你可以定义多种奖励函数并传入 GRPOTrainer 进行模型训练。

设计奖励函数堪称是搭建 GRPO 训练流程中最具成就感的环节 ------ 双关语警告🙂。

10 Cargo 奖励函数

正如文章开头所述,我们将利用 Cargo 工具链构建奖励机制。由于奖励函数可定义为纯 Python 函数,因此我们可直接通过 subprocess 模块调用 Cargo 工具。

完整代码实现详见 GitHub:github.com/Oxen-AI/GRP...

我们定义了一个 RustTool 类,支持执行 cargo build、cargo clippy 或 cargo test 命令。其 run() 方法将返回包含工具执行结果(通过/失败状态信息、错误信息)的字典。

该工具应用于为每个测试用例临时创建的 Rust 项目目录。项目的初始化与清理流程包括:创建目录→写入 main.rs 和 Cargo.toml 文件→在其中写入代码与单元测试→执行后清理。 欲知更多详情,可查阅完整代码中的 setup_and_test_rust_project 函数。

在具备初始化和清理 Rust 小型项目的能力后,需将其与 GRPO 奖励函数对接。奖励函数接收一批提示词(prompts)与生成内容(completions),要求对每组 prompt+completion 进行评分。

持久化记录 prompts 和 completions 有助于帮助我们理解 GRPO 算法的内部机制。每个 prompt 将生成 N 个 completions。假如我们设置 N=4,这样模型就有 4 次生成内容的机会。

以下方日志中的 task_8347 为例,模型尝试了 4 次函数代码的实现,最终成功一次。GRPO 会对正确的解决方案进行奖励,从而促使模型性能持续提升。 使用红色标记圈出来的部分是三个单元测试失败的案例,绿色标记的部分则为测试通过的案例:

使用相同的工具与逻辑,我们为 cargo build、test 和 clippy 分别构建奖励函数。同时设置验证机制确保代码与单元测试不是空的,且单元测试必须包含 assert! 断言,防止模型通过 println! 语句作弊。

所有训练结果均实时记录至 Oxen.ai,以便绘制模型训练曲线(如文章开篇所示)。在模型训练过程中,我们会通过滚动平均(译者注:Rolling Average,是一种统计方法,用于对时间序列数据中的短期波动进行平滑处理。) 的方式对数据进行平滑处理、计算,以此观察模型在特定奖励指标上的改进趋势。

例如编译通过率刚开始在 30-40% 之间波动,随着训练逐步攀升至 70%:

单元测试通过率的提升相对滞后,且通过率的波动范围更大:

需要观察模型在每个奖励维度(如代码编译通过率等)上的改进情况,定期抽查模型接收的提示词(输入)及其生成的代码(输出)。为此我们编写了一个 @experiment.log 装饰器,在奖励函数执行时自动将结果写入 jsonl 文件并提交至 Oxen.ai

该装饰器每次调用函数时将结果写入指定文件,训练循环(training loop)中的回调函数每 N 步将数据提交至 Oxen.ai。由于数据按时间顺序存储,需翻页至末尾查看训练后期的生成样本。部分输出示例:www.oxen.ai/ox/Rust/fil...

11 训练效果评估

此前设定的 baseline ------ 未经训练的 Qwen/Qwen2.5-Coder-1.5B-Instruct 模型在代码编译通过率和单元测试通过率上分别仅为 61% 和 22%。

经过一轮 GRPO 训练后,编译通过率提升至 80%,单元测试通过率达 37% 🎉。 通过一轮训练即实现 20% 与 15% 的绝对准确率提升。

仅仅定义了几个奖励函数和使用较小规模的数据集,这样的训练效果已经相当不错了。您可通过此处[6]探索 1.5B 模型的原始输出结果。

此次实验成果令人鼓舞。整个训练耗时约 24 小时,成本小于 100 美元(下图是 H100 实例的每小时成本,仅供参考):

该实验表明:GRPO 算法对普通开发者来说也比较实用 ------ 开发者可灵活定义任意奖励函数,即使在小模型上也能实现性能的明显提升。

Thanks for reading!

Hope you have enjoyed and learned new things from this blog!

END

本期互动内容 🍻

❓如果让你为代码生成任务设计奖励函数,您会优先考虑哪个指标?为什么?

🔗文中链接🔗

1\][github.com/Oxen-AI/GRP...](https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2FOxen-AI%2FGRPO-With-Cargo-Feedback%2Ftree%2Fmain "https://github.com/Oxen-AI/GRPO-With-Cargo-Feedback/tree/main") \[2\][arxiv.org/abs/2501.04...](https://link.juejin.cn?target=https%3A%2F%2Farxiv.org%2Fabs%2F2501.04519 "https://arxiv.org/abs/2501.04519") \[3\][www.oxen.ai/ox/Rust/fil...](https://link.juejin.cn?target=https%3A%2F%2Fwww.oxen.ai%2Fox%2FRust%2Ffile%2Fmain%2Fcargo_test_passed_train.parquet "https://www.oxen.ai/ox/Rust/file/main/cargo_test_passed_train.parquet") \[4\][www.oxen.ai/ox/Rust/fil...](https://link.juejin.cn?target=https%3A%2F%2Fwww.oxen.ai%2Fox%2FRust%2Ffile%2Fmain%2Fresults%2FQwen2.5-Coder-1.5B-Instruct%2Fresults_code_and_tests.parquet "https://www.oxen.ai/ox/Rust/file/main/results/Qwen2.5-Coder-1.5B-Instruct/results_code_and_tests.parquet") \[5\][www.oxen.ai/ox/Rust/fil...](https://link.juejin.cn?target=https%3A%2F%2Fwww.oxen.ai%2Fox%2FRust%2Ffile%2Fgpt4-5-results%2Fresults%2FGPT4.5%2Fpredictions.parquet "https://www.oxen.ai/ox/Rust/file/gpt4-5-results/results/GPT4.5/predictions.parquet") \[6\][www.oxen.ai/ox/Rust/fil...](https://link.juejin.cn?target=https%3A%2F%2Fwww.oxen.ai%2Fox%2FRust%2Ffile%2Fmain%2Fresults%2FGRPO_82_2025-03-02_22-49-17_Qwen2.5-Coder-1.5B-Instruct%2Fresults_code_and_tests.parquet "https://www.oxen.ai/ox/Rust/file/main/results/GRPO_82_2025-03-02_22-49-17_Qwen2.5-Coder-1.5B-Instruct/results_code_and_tests.parquet") **原文链接:** [ghost.oxen.ai/training-a-...](https://link.juejin.cn?target=https%3A%2F%2Fghost.oxen.ai%2Ftraining-a-rust-1-5b-coder-lm-with-reinforcement-learning-grpo%2F "https://ghost.oxen.ai/training-a-rust-1-5b-coder-lm-with-reinforcement-learning-grpo/")

相关推荐
袁庭新4 小时前
我用Cursor + DeepSeek + Claude-3.7-Sonnet + DevBox,10分钟开发了一个系统
claude·cursor·deepseek
量子位4 小时前
LIama 4 发布重夺开源第一!DeepSeek 同等代码能力但参数减一半,一张 H100 就能跑,还有两万亿参数超大杯
人工智能·deepseek
云道轩4 小时前
deepseek为采用JAVA重构模型运营平台vLLM和SGLang指定的计划
java·vllm·deepseek·sglang
掘金安东尼4 小时前
记录 Chatgpt 辅助离线配置环境,我就像是个小丑🤡🤡🤡
人工智能·程序员·llm
无名之逆7 小时前
在Rust生态中探索高性能HTTP服务器:Hyperlane初体验
运维·服务器·开发语言·后端·http·rust·自动化
浪淘沙jkp8 小时前
大模型学习五:‌DeepSeek Janus-Pro-7B 多模态半精度本地部署指南:环境是腾讯cloudstudio高性能GPU 16G免费算力
学习·deepseek·janus-pro·janus-pro-7b
YungFan10 小时前
SwiftUI-MLX本地大模型开发(二)
macos·llm·swiftui
go4it10 小时前
聊聊Spring AI的PgVectorStore
llm
Coding_Hao10 小时前
window基于wsl部署vllm流程及踩坑经历(包含cuda toolkit、nvcc版本问题)
llm