GPU 内核,第二部分:用监督微调和 GPU 训练场教会 27B 模型写 Triton 内核

Dev.to ML 2026-11-04T15:54:05.253759

在第一部分里,我们剖析了 GPU 内核的底层原理——为什么算子融合和内存分块如此重要,以及 KernelBench 这类基准测试如何评估大语言模型(LLM)写出的内核是否真能跑赢 PyTorch。本文是动手实践的续篇。我们拿开源模型 Qwen3.8-27B 开刀,分两个阶段训练它写 Triton 内核:先是在 2,034 个高质量内核上做监督微调(SFT),这些内核全部在 NVIDIA H100 上重新验证过;然后把它放进一个自动化的 GPU 训练场做强化学习(RL),每个候选内核都会经过编译、正确性验证和性能跑分。

在标准的 200 道 KernelBench 题目上评估(每题只做一次贪婪解码),所有主要指标都在往好的方向走。整体正确率从 21.5% 提升到 29.5%。既数学正确又比原生 PyTorch 跑得快的内核,占比从 7.0% 涨到 9.5%。在 Level 1(单个独立算子)上,模型现在能解出 47 道题,此前是 31 道;能跑赢 PyTorch 的有 15 道,此前是 8 道。

但与此同时,前沿商业模型仍然保持明显领先。下面我们会拆解数据集和验证训练场是怎么搭的,每个训练阶段各自取得了什么成果,奖励设计里那些微妙坑——训练场一度教会了模型错误的策略——以及要抹平剩下的性能差距还需要做些什么。

开源权重与数据:发布的检查点已上传至 Hugging Face,名为 Qwen3.8-27B-GFT-Triton-Kernels-LoRA,附带可直接用于 vLLM 的配置和我们完整的评估工具链。SFT 训练集也已开放,名为 GFT-Triton-Kernels-SFT-H100-Verified:包含 2,034 对 PyTorch 到 Triton 的转换样本,每一对都在 H100 上编译并验证过。本文提到的所有指标都收录在 JSON 下载包里。

KernelBench Triton,Level 1–2(共 200 题),每题一次贪婪回答,H100,FP32。对比对象:基座模型、SFT 之后、以及 SFT + RL 之后(即发布的检查点)。

指标 基础模型 SFT 后 SFT + RL 后 变化
编译并成功运行的答案 79.5% 86.5% 88.5% +9.0 个百分点
正确(fast₀) 21.5% 26.0% 29.5% +8.0 个百分点
正确且快于 eager PyTorch(fast₁) 7.0% 8.0% 9.5% +2.5 个百分点
… 快于 1.5 倍以上(fast₁.₅) 5.0% 5.0% 6.0% +1.0 个百分点
… 快于 2 倍以上(fast₂) 3.5% 4.5% 5.0% +1.5 个百分点
正确且快于 torch.compile 12.5% 15.0% 16.0% +3.5 个百分点
Level 1 正确 31.0% 43.0% 47.0% +16.0 个百分点
Level 1 快于 eager 8.0% 13.0% 15.0% +7.0 个百分点
Level 2 正确 12.0% 9.0% 12.0% ±0.0 个百分点
Level 2 快于 eager 6.0% 3.0% 4.0% −2.0 个百分点

从这里开始

我们如何构建一套在目标 GPU 上验证过的 SFT kernel 数据集;kernel gym 如何给答案打分并转化为奖励;七个 gym 版本以及各自失败的原因;SFT 和 RL 在 KernelBench 上分别改变了什么;Level 2 为何没有提升;前沿模型在同样 200 道题上的表现对比。

1. 评分标准

为了保证比较的严谨和公平,本文中每个模型都使用完全相同的测试框架:KernelBench(第 1、2 级,Triton 后端),提示词逐字节相同,并在同一块 NVIDIA H100 SXM 上运行同一套验证程序。第 1 级覆盖 100 个独立算子(矩阵乘法、卷积、归一化、激活函数和损失函数)。第 2 级测试 100 个复合模块——通常是一个大型 GEMM 或卷积,后面接一段逐元素运算,这种结构下算子融合有明显优化空间。(第 1 部分详细介绍了 KernelBench 如何评估提交结果,以及对比基准时需要注意的关键点)。

本文中每个 Qwen 检查点的评估协议。

基准 KernelBench 修订版 423217d,Triton 后端,第 1–2 级,共 200 道题
提示 KernelBench 针对 FP32 的一次性 Triton 提示,系统提示为“You are a helpful assistant”
解码 vLLM,贪心解码(temperature 0),关闭思考模式,最多生成 8,192 个新 token,每题一个答案
正确性 5 组随机输入,FP32,atol = rtol = 1e-4
计时 100 次 CUDA event 运行取均值,对照同一 GPU 上的 eager PyTorch 和 torch.compile(mode="max-autotune")
硬件 NVIDIA H100 80GB HBM3;PyTorch 2.9.1,Triton 3.5.1,CUDA 13.0

两个训练阶段与基准测试。每个阶段都从上一个适配器继续训练;每个方框下方的数字是该检查点在全部 200 道题上的 KernelBench 得分。

2. SFT 和 RL 带来了什么变化

未经训练时,基础版 Qwen3.8-27B 已经能理解基本的 Triton 语法——大约五分之四的生成答案能编译并运行,没有语法错误。它欠缺的是数值精度(正确处理边界条件和索引计算)以及击败 PyTorch 所需的架构直觉。我们的两阶段训练同时解决了这两个问题:

基础模型(灰色)、SFT 后(浅蓝色)以及 SFT + RL 后(蓝色)在同样 200 个问题上的表现。所有总体指标都有提升;Level 1 提升最大。Level 2 的正确率没有变化。

监督微调为正确率打下了基础(Level 1:31 → 43),RL 则进一步打磨了边缘情况的处理。由于每个检查点都在完全相同的问题集上评估,我们可以做配对 bootstrap 分析。与 SFT 基线相比,最终的 RL 检查点成功解决了之前失败的 8 个问题,只有 1 个出现回退:

在同一组 200 个问题上相对 SFT 适配器的变化,单位为百分点,附 95% 配对 bootstrap 区间(对问题重采样 100,000 次)。一次 300 步 RL 训练中的四个检查点。

RL 检查点 正确率 比 eager 快 比 torch.compile 快
Step 100(已发布) +3.5 [+1.0; +6.5] +1.5 [−0.5; +4.0] +1.0 [±0.0; +2.5]
Step 150 +3.0 [±0.0; +6.0] +1.5 [±0.0; +3.5] ±0.0 [−2.0; +2.0]
Step 200 +1.0 [−1.5; +3.5] +0.5 [−1.0; +2.5] +0.5 [−1.0; +2.5]
Step 300 +1.5 [−2.0; +5.0] ±0.0 [−2.0; +2.0] −1.0 [−3.0; +1.0]

引导重采样得到的置信区间说明,第 100 步的正确率提升在统计上是有意义的:提升了 3.5 个百分点,95% 置信区间为 [+1.0, +6.5]。相比 SFT,速度提升更有限,置信区间下界几乎贴到零。第 100 步之后性能进入平台期;由于我们是在能看到后续 checkpoint 的情况下挑选了第 100 步,因此它的数字反映的是整轮运行中的最优选择。Level 2 仍是明显的例外:整体正确率保持在 12 道题,但快速 kernel 从 6 个略微降到 4 个。第 6 节会拆解这一平台期背后的机制。

3. 第一阶段:在目标 GPU 上验证过的 SFT 数据集

公开网页抓取的 kernel 数据集纸面上看很可观,但有一个严重的资质问题:大多数样本只用极小的玩具输入验证过,或者在消费级 GPU 上用宽松容差跑过,甚至从未真正执行。为了让 SFT 教会模型真实的 GPU 行为,我们构建了一个过滤后的数据集,其中每一个样本都必须在 NVIDIA H100 上以生产规模通过严格的执行检查:

6,875 个公开答案如何变成 1,352 条训练行,外加我们早先 gym 运行中得到的 682 个成功 kernel。

最终数据集包含 2,034 个已验证配对——其中 1,624 个在目标规模下优于 torch.compile——全部控制在 4,096 token 的上下文窗口内。我们在单张 H100 上训练了一个 rank-8 的 LoRA 适配器,2 个 epoch(3,674 步,学习率 2e-4),耗时约五小时。

4. 第二阶段:内核健身房

SFT 展示了结构良好的内核长什么样,而强化学习则赋予模型自主权:去实验、去调试编译器报错、去接收即时奖励信号。我们的内核健身房采用 OpenEnv 风格的架构:每个回合中,一个 worker 提供参考 PyTorch 模块,模型起草一份实现,然后一个隔离的 GPU 子进程在 H100 上对候选方案进行基准测试。

一个回合。任务是 PyTorch 模块;模型回答一次,或用工具修复;五道验证关卡决定答案能得多少分;奖励中的速度占比从 0.75× 到 4× 对数增长。

任务

这个训练环境会生成十四类不同的任务,覆盖 KernelBench 第 1 级和第 2 级涉及的操作范围。每个任务都由程序化种子动态实例化,种子中规定了数学算子、张量形状(规模从 100 万到 3200 万个元素,确保执行时间主要反映内存带宽,而非内核启动的抖动)以及测试数据的分布。对于那些模型一开始几乎无法完成的困难操作,我们引入了分阶段的课程变体:smallshape(小形状)、scaffold(脚手架,提供主机端设置和 tile 偏移,内部数学运算留空),以及 epilogue(尾声任务,核心库 GEMM 保持不变,只要求用 Triton 实现融合的激活尾部)。

最终训练环境中的任务家族如下:

家族 示例 分阶段变体
逐元素 ReLU、GELU、LeakyReLU、sigmoid —
归约 行求和、均值和最大值;列求和 —
扫描 行和列的累积求和 —
归一化 RMSNorm、LayerNorm —
softmax softmax、log-softmax —
融合 SiLU 和 GELU 门控、add + ReLU —
矩阵乘法 matmul、转置 matmul small、scaffold
GEMM 尾声 linear + ReLU 或 GELU,带残差和缩放 small、epilogue
conv1d 深度可分离 conv1d,带偏置、ReLU 或残差 small、scaffold
conv2d 3×3、步进和逐点卷积 small、scaffold
pool2d 最大池化和平均池化 —
模块 linear 和 conv2d 模块、分组卷积 small、scaffold
融合混合 conv + ReLU + 偏置、linear + GroupNorm + ReLU、linear + max + GELU;允许调用一次 cuBLAS/cuDNN small、epilogue
KernelBook 1,201 个经过验证的真实模块,输入规模扩大到约 100 万个元素 —

两种作答方式

训练片段以两种互补的交互格式运行:

最终训练中,模型解决了 63.8% 的修复回合,而单轮模式下只有 50.5%。这说明编译器反馈能把那些差一点就成功的实现救回来。

五道关卡

  1. 静态策略沙箱:只允许白名单内的导入;私有 PyTorch 内部接口和任意子进程调用在执行前就会被拦下。

  2. 编译与启动验证:代码必须干净编译,执行时必须确实在 GPU 上启动 Triton 核,这一点通过 CUDA 运行时事件来验证。

  3. 计算隔离:forward() 执行期间禁用 PyTorch 原生计算(混合任务中允许一次 cuBLAS/cuDNN 调用),确保实际算术由 Triton 核完成。

  4. 五个隐藏测试用例:覆盖正常规模、3 倍规模、0.01 倍规模、符号取反和绝对值,FP32 容差为 1e-4(按数值量级缩放)。这样能防止硬编码查表的作弊手法,也能验证非线性激活的两个分支。

  5. 低方差计时:候选核、eager PyTorch 和 torch.compile(mode="max-autotune") 在同一块 GPU 上基准测试。只有运行间方差低于 10%(微型核上为 30 微秒)时,计时结果才会被接受。

奖励

奖励函数在正确性和性能之间取平衡。第 1 到第 3 关是硬性要求:如果代码编译失败、在计算上作弊或违反沙箱规则,奖励立刻归零。

其中,c 表示通过隐藏测试用例的比例,v 是相对基线的实测加速比,ρ 用来检查计时稳定性,e 则惩罚不必要的工具调用折腾。速度奖励有严格门槛:只有五个隐藏测试用例全部稳定通过,内核才能拿到速度分。对于复杂任务族,如果一组八个回答全部失败,那么在 1e-2 的宽松容差内答对部分也算部分进展分——这能提供梯度信号,把「差一点就对」和「语法完全崩掉」区分开。

RL 训练配方

下面是发布检查点所用的 RL 训练设置。

起点 SFT 适配器(LoRA r = 8,alpha = 16,覆盖 q、k、v、o、gate、up 和 down 投影)
算法 GRPO 式分组,每个任务 8 个回答,每步一个任务,CISPO 损失,REPO-R 熵控制,KL β = 0.08
优化器 学习率 1e-5,线性调度,300 步;每 10 步存一次检查点
采样 温度 1.0,最多 6,144 个补全 token,关闭思考;八个回答得分全相同的组会重新采样
模型 Qwen3.8-27B,4-bit NF4 加 BF16 LoRA(QLoRA),单张 H100 负责训练、生成和评分
实际耗时 300 步约 28 小时;300 步中有 294 步拿到了非零梯度

这套训练配置建立在我们之前的多奖励 RL 研究之上:CISPO 约束策略权重更新,同时不丢弃有信息量的 token;REPO-R 则维持探索熵。

5. 七个 gym 版本,以及各自被什么搞坏

前面说的验证环境是第 9 版。走到这一步,中间踩过七种不同的失败模式。正如我们在验证器手册里记录的那样,RL 智能体会毫不留情地利用环境里的每一个漏洞。要抓住奖励泄漏,唯一可靠的办法是在整个训练过程中直接检查生成的代码:

每个 gym 版本、我们在前一版中发现的问题,以及做了哪些改动。

版本 上一版发现的问题 改动
v3 复制参考输入辅助函数的答案被当作不安全的代码提交而拒绝。 复制的辅助函数会被干净地剥离,而不是被惩罚。
v4 FP32 容差过于宽松,导致一些内核通过了测试,但后来被 KernelBench 拒绝。 FP32 输出必须在 KernelBench 严格的 1e-4 容差内与参考张量匹配。
v5 在困难的算子族上,一组中全部 8 个样本经常得零分(例如使用 TF32 的矩阵乘法未达到 1e-4),导致梯度为零。 在放宽的 1e-2 范围内匹配的内核可获得部分进度分,但严格低于已验证解。
v6 速度奖励 clip(speedup − 1) 在 2 倍处饱和;在约 30 万元素的张量上,编译开销让几乎任何内核看起来都比 torch.compile 快 6–9 倍。已解出的内核无论质量如何都得分约 1.0,速度优化停滞。 将张量规模调整到 1–32M 元素;引入对数速度奖励,范围从 0.75 倍到 4 倍;内存使用量决定奖励资格。
v7 没有任何机制验证 Triton 内核确实在 GPU 上运行,允许答案通过,而 PyTorch 在回退路径中静默处理计算。 引入原生执行门控:GPU 内核启动必须通过 CUDA 事件确认。移除仅宽松匹配的得分和对慢速内核的奖励。
v8 训练环境与 KernelBench 出现偏差:提示不同,且缺少 2D 卷积/模块。审计发现内部 PyTorch 导入可能绕过计算保护,且一个种子 bug 只采样了 966 行 KernelBook 中的 69 行。 新增 14 个算子族,包括 conv2d、pool2d、scan、模块、混合类型和 KernelBook;新增单轮模式,提示与 KernelBench 逐字节一致;新增第五个隐藏用例(|x|);在混合类型中实现计算保护,仅允许恰好一次库调用;强制执行严格的导入白名单;修复行哈希。
v9 复杂算子族(conv2d、模块、混合类型)的解决率仍接近零;不稳定或部分正确的内核偶尔能排在组内第一。

| 引入分阶段的课程练习;速度奖励只发给稳定且完全验证通过的 kernel;新增按完整执行图构建的分布外(OOD)留出集。

有两种失败模式,可以说明奖励机制里的 bug 有多隐蔽:

在训练场(gym)里,v9 的分阶段练习成功解决了冷启动问题。此前产出零个有效样本的算子族,开始稳定地提供梯度反馈:

分阶段练习前后训练答案的解决率,相同的 SFT 起点与设置。脚手架和收尾部分多数时候能解出;完整任务的提升较小。

6. 训练场与 KernelBench 的分歧

我们大部分的基准提升出现得很早:集中在监督微调阶段和 RL 的前 100 步。此后,训练场的指标还在继续爬升,但 KernelBench 上的表现却停滞了。在 v9 训练的四个季度里,训练场的解决率依次为 73.4% → 59.0% → 54.9% → 61.7%,而对应的 KernelBench 检查点 fast₁ 得分只有 9.5%、9.5%、8.5%、8.0%。

为什么在训练场里多做 RL 训练,却没能迁移到基准上?复盘之后,我们发现训练环境和 KernelBench 之间存在三处结构性错配:

6.1 另一种速度对照目标

这个 gym 衡量加速比时,对照的是 torch.compile(mode="max-autotune");而 KernelBench 对照的是普通(非编译)PyTorch。在 gym 所用的大尺寸 FP32 张量下,编译后的 PyTorch 反而常常是更慢的基线:

正确的训练 kernel 中位加速比,按 kernel 家族分组:蓝色为对比 torch.compile,红色为对比 eager PyTorch。14 个家族中有 8 个,eager 的中位数接近或低于 1×。

86.8% 的正确训练 kernel 打败了 torch.compile,但只有 61.4% 打败了 eager PyTorch。事实上,通过测试的 kernel 里有 26.2% 拿到了正的加速奖励,实际却比 eager 模式跑得慢。编译后运行时间的中位数比 eager 慢 2.1 倍。也就是说,奖励函数教模型跨过的门槛,比实际测试的门槛更容易。

6.2 Level 2 上的不同策略

KernelBench Level 2 包含 63 个卷积任务和 36 个矩阵乘法任务,每个任务都带一段逐元素的后处理尾部。要在原始卷积或大矩阵乘法上超过 NVIDIA 手工调优的 cuDNN 和 cuBLAS,难度极高。最有效的架构选择是混合实现:核心的矩阵乘法或卷积交给 cuBLAS/cuDNN,只把融合后的激活尾部写成自定义 Triton kernel。

仅 Level 2。左图:保留 PyTorch 层或算子(通常是卷积或矩阵乘法)的答案占比。右图:比 eager 更快的正确答案,分为混合实现(蓝色)和纯 Triton(青柠色)。

早期,基础版 Qwen 自然就采用了这个策略,在 Level 2 上有 55% 的提交是混合实现。事实上,早期的一个检查点(gym v5)产出了 11 个快速的混合解——是所有 Qwen 模型里 Level 2 的最好成绩。

遗憾的是,我们后来的数据过滤和训练环境规则恰恰惩罚了这种行为。在整理 SFT 数据时,我们把混合代码过滤掉了;在训练环境里,也严格禁止调用 PyTorch 做计算。结果,SFT 模型只有 8% 的时候会提交混合方案,其余时候都在尝试用 Triton 从零重写完整的二维卷积——这种代码既容易出数值 bug,又明显比 cuDNN 慢。相比之下,Claude Opus 和 GPT-6 这类前沿模型在 Level 2 的回答里有 61% 到 93% 采用了混合方案,它们的高分几乎全靠这个策略。

6.3 练习题不等于考试

分阶段的课程练习(脚手架和尾声部分)确实为复杂算子打开了训练信号,但收益主要集中在那几个有辅助的变体上。到了基准评测里面对完整的、无辅助的 Level 2 模块时,模型就很难独立写出端到端的完整 kernel。一套有效的课程,最后一个阶段必须尽量贴近真实目标分布——全尺寸、无辅助。

我们评测的全部十一个 Qwen checkpoint:从基座模型做 RL、SFT、以及 SFT 之后再接 RL(两个训练环境版本)。SFT 和 SFT + RL 在正确性上领先;从基座模型做 RL 在 fast₁ 上领先,一路到 Level 2 混合方案。

7. 前沿模型在同样 200 道题上的表现

为了给这些结果一个参照,我们用同一个 H100 执行评分器,把四款前沿商业模型放到完全相同的 200 道 KernelBench 题上跑了一遍。这些模型通过各自的 CLI 接口调用,使用标准推理模式,所以这并非与贪心解码、非推理模式的 Qwen 做严格逐参数对齐的对比。尽管如此,这个比较仍能提供有价值的架构层面洞察:

每个难度级别中正确、以及正确且比 eager 更快的答案数量(满分 100)。前沿模型的 fast₁ 不含被标记为速度反常的答案。

Level 1–2,共 200 道题。fast₁ 按评分器的口径,把被标记的加速(超过 10 倍)计入;「未标记」一列则把这些结果剔除。

模型 正确率 fast₁ 相对 eager fast₁(未标记) fast₂ fast₁ 相对 torch.compile 平均答案 token 数
Qwen3.8-27B base 21.5% 7.0% 7.0% 3.5% 12.5% 未记录
Qwen3.8-27B SFT + RL 29.5% 9.5% 9.5% 5.0% 16.0% 1,109
Fable 5.1 70.0% 46.5% 43.0% 17.5% 43.5% 2,906
Opus 5.5 68.5% 51.5% 47.5% 22.5% 41.0% 2,535
GPT-6 Astra 84.0% 66.5% 63.5% 19.5% 50.0% 1,871
GPT-6.1 Sol 85.0% 66.5% 62.5% 20.0% 51.0% 1,747

8. 下一步我们会改什么

  1. 让训练环境和基准的基线对齐:改为以 eager PyTorch 为基准(或采用 eager + 编译的联合目标),这样奖励就不会再鼓励那些被 KernelBench 判定为慢的 kernel。训练环境现在已经支持这个开关,后续训练可以直接使用。

  2. 拥抱混合模式:在 Level 2 类任务中,允许调用 cuBLAS/cuDNN 等库函数来处理大型 GEMM 和卷积,对融合的 elementwise 尾部给予奖励,并把这些混合实现保留在 SFT 语料里,而不是把它们过滤掉。

  3. 让课程锚定目标规模:早期探索阶段可以保留分阶段的脚手架,但要确保最终训练阶段在目标生产尺寸上给无辅助的完整任务足够权重。

  4. 多采样评估:每个问题只评估一个贪婪样本会引入噪声,1–3 个问题的差异很难解读。对每个任务用三个随机种子各跑一遍再取平均,虽然更费算力,但能更清楚地区分不同 checkpoint。

9. 局限

本研究每张图表背后的遥测与配置数据都在 JSON 下载 中提供。模型权重和经过验证的训练数据集已在 Hugging Face 上开源:LoRA adapter 和 verified SFT dataset。


原文发表于 g-ftech.com。

查看原文