微调提速 6 倍:Qwen 27B 从 7 小时 44 分到 1 小时 17 分

微调提速 6 倍:Qwen 27B 从 7 小时 44 分到 1 小时 17 分

原文:Databricks Blog - 《Reducing Qwen training time by 6x on AI Runtime》(https://www.databricks.com/blog/reducing-qwen-training-time-6x-ai-runtime)

一、起点:GPU 利用率已经 98%

Databricks 这条优化的基线很值得注意:微调 Qwen 3.8 27B、固定数据集,一次训练 7 小时 44 分钟,GPU 平均利用率 98%,吞吐 9,396 tokens/s。任何做过性能优化的人看到 98% 都会先愣一下------利用率这么高,还有什么可挤的?

答案是有的,而且挤出了 6 倍。这也直接说明了第一条经验:GPU 利用率不等于没有浪费,它只告诉你 GPU 在干活,不告诉你它在干什么活。

原文没有给出具体的卡型和卡数,只说明在 27B 这个量级上,纯数据并行放不下权重,所以用 FSDP2 把模型分片到多卡。

二、第一步不是优化,是测量

优化从一次单步 trace 开始,用 torch.profiler 抓一个 step 的 CPU 与 CUDA 活动:

python 复制代码
with torch.profiler.profile(
    activities=[
        torch.profiler.ProfilerActivity.CPU,
        torch.profiler.ProfilerActivity.CUDA,
    ],
    schedule=torch.profiler.schedule(
        wait=1,      # 先跳过 1 步,让运行时预热
        warmup=0,    # 这次单步 trace 不做额外 warmup
        active=1,    # 只抓 1 个 step
        repeat=1
    )) as prof:
  <training code here>
print(prof.key_averages().table(sort_by="self_cuda_time_total", row_limit=10))

torch.profiler.schedule 这几个参数是配套用的:wait 让前几步跑完预热,active 决定抓几个 step,repeat 决定这套 schedule 重复几轮;sort_by 换成本次关心的 self_cuda_time_total,row_limit 只留前十名,避免一次打印几千行。

trace 给出的结果是这样的:CPU 侧 14.4 秒、1,714,382 次调用;GPU 侧 10.3 秒、269,448 个算子。CPU 的调用次数是 GPU 算子数的六倍多------瓶颈不在算力,在"按 token 逐个调度"这件事本身的固定开销。

三、优化一:给非标准层装对口的内核

第一个改动很小:装上 flash-linear-attention 和 causal-conv1d 两个包。原因是 Qwen 3.8 的某些层不是标准注意力,而是 gated delta network 这类线性注意力结构,通用算子在它们上面效率不高。

装上之后端到端时间从 7 小时 44 分降到 2 小时 5 分,提速 3.7 倍。这一步没有任何算法改动,纯粹是让算子对上硬件。非标准层要配对应的融合内核,这条在任何自定义架构上成立。

四、优化二:把 2D 的 padding 换成 1D 打包

第二个改动看着更不起眼,但收益同样大。原来的数据加载器把样本打包成 2D 矩形张量,每批 padding 到 8192 个 token;padding 出来的 token 也要参与前向和反向,等于白烧算力。

改法是把 2D 矩形张量换成 1D 的打包序列,再传入正确的 position_ids,让每个样本只 attend 到自己内部的 token。padding 本身也在消耗算力,而且这一改动把总批次数减少了大约 36%。

结果:2 小时 5 分降到 1 小时 17 分。两次改动叠起来,就是 7 小时 44 分到 1 小时 17 分,总体 6 倍。

优化后的单步 profiling 也变干净了:CPU 调用从 1,714,382 次降到 118,316 次,GPU 算子从 269,448 个降到 18,060 个,CPU 与 GPU 两边的耗时都降到 3.9 秒。

五、这套配置本身就是性能的一部分

除了上面两步,原文给出的训练骨架也值得抄一遍,因为分片、检查点和精度策略本身就是性能的一部分:

python 复制代码
# 数据加载器
data_loader = make_dataloader("HuggingFaceH4/ultrachat_200k")

model = AutoModelForImageTextToText.from_pretrained(
    "Qwen/Qwen3.8-27B", torch_dtype=torch.bfloat16)

# 梯度检查点,压低显存占用
model.gradient_checkpointing_enable(
    gradient_checkpointing_kwargs={"use_reentrant": False})

# FSDP2:逐层分片,再对整体分片
mp = MixedPrecisionPolicy(param_dtype=torch.bfloat16, reduce_dtype=torch.float32)
layers = model.model.language_model.layers
for layer in layers:
  fully_shard(layer, mp_policy=mp)
fully_shard(model, mp_policy=mp)
model.cuda()

opt = torch.optim.AdamW(
    model.parameters(), lr=1e-5, betas=(0.9, 0.95), weight_decay=0.0)

model.train()
for batch in data_loader:
  out = model(input_ids=batch["inputs"], labels=batch["labels"])
  out.loss.backward()
  opt.step()
  opt.zero_grad(set_to_none=True)

几处细节:fully_shard 先逐层调用、再对整个模型调用,这是 FSDP2 的标准顺序,先分层再整体才能让参数、梯度和优化器状态都按同一套分片走。MixedPrecisionPolicy 里参数用 bfloat16、梯度归约用 float32,是为了在省显存的同时不让归约精度拖累收敛。use_reentrant=False 是新一代梯度检查点的写法,前者已不推荐。zero_grad(set_to_none=True) 把梯度置为 None 而不是零,省一次写显存。

六、四条能直接搬走的经验

  • 只看 GPU 利用率会骗人:98% 的利用率下仍然有 6 倍空间,必须看时间花在哪;
  • 单步 trace 就够:不用跑完整训练,抓一个 step 的 CPU 与 CUDA 活动就能定位大头;
  • 自定义结构要主动找融合内核:线性注意力、卷积之类的非标准层,通用算子很容易成为瓶颈;
  • padding 是隐性浪费:矩形打包看起来整洁,实际上让模型算了一堆无意义的 token。

七、小结

这次提速没有引入任何新的算法或模型技巧,三件事分别对应"测量、算子、数据管线"。真正有价值的是顺序:先测量再动手,而不是先换框架或调超参。

对做 Agent 的同学来说,这条链路同样有用------给 Agent 配一个自己微调的小模型时,训练时间直接决定迭代速度,而上面这些优化大多不依赖特定平台,本地多卡环境也能照做。具体 API 与推荐配置请以 PyTorch 与 Databricks 官方文档为准,本文未验证最新版本。

相关推荐
李纲明13 小时前
WordPress 图片拖慢首屏与磁盘:媒体库体积、缩略图爆炸与懒加载排查
性能优化·wordpress·webp·图片优化·媒体库
赵大仁14 小时前
官网性能优化实战:LCP 3.2s → 2.1s、CLS 0.107 → 0,以及被我推翻的 6 个判断
前端·性能优化·cls·lcp·core web vitals
CCblandy18 小时前
一致性哈希到底省了多少搬迁?本机实测 3 轮:取模 6→7 搬 85.8%,V=128 只搬 14.3%,虚拟节点太少会被倾斜骗
后端·python·云原生·性能优化·架构
Linux实战笔记20 小时前
压测后我对GraphQL vs REST API 设计改了灵活查询与性能权衡
性能优化·graphql·rest api·接口设计·n+1 问题
CCblandy2 天前
Agent 工具调用怎么编排?本机实测:并行只到 2.63 倍,慢的先发省 30ms,CPU 活加 worker 无效
人工智能·后端·python·性能优化
用AI来创业吧2 天前
网站建设公司哪家好:报价差 3 倍,差在哪四项成本上(选型笔记)
笔记·性能优化·软件工程·安全威胁分析
yunwei372 天前
eBPF 入门实践教程:BPF Token,安全的委托式权限与程序加载
linux·后端·性能优化
CSharp精选营2 天前
SaveChanges 一万行?换批量操作快 10 倍
性能优化·efcore·批量操作·csharp·executeupdate
爱喝水的鱼丶2 天前
SAP-ABAP:S/4 HANA ABAP 新特性入门:与 ECC ABAP 的核心差异、架构优势与学习路径规划
运维·数据库·性能优化·sap·abap·hana·开发交流