为什么选择 Qwen 系列在 ROCm 上“死磕”
在 AMD GPU 上做模型微调,最大的痛点往往不是环境配不通,而是“跑起来了但效果不对”。最近我把重心放在了 Qwen 系列模型(特别是 Qwen-1.5/2 的 7B 和 14B 版本)在 ROCm 环境下的适配上。相比 Llama 系列,Qwen 的某些算子实现在非 CUDA 环境下更容易出现数值抖动,但这恰恰是验证 LLaMA-Factory 与 ROCm 深度结合的最佳试金石。
这次实践的核心目标很明确:不依赖任何“黑盒”封装,利用 LLaMA-Factory 原生的 ROCm 支持,配合手动调优,让 Qwen 在 MI300X 或 RX 7900 系列显卡上收敛得和在 A100/H800 上一样稳。过程中踩过的坑,主要集中在超参数敏感度和精度溢出这两块,下面直接上干货。
学习率与 Batch Size 的“微妙平衡”
在 NVIDIA 卡上跑通 Qwen 的 LoRA 微调,通常默认学习率设在2e-4到5e-4之间就能有不错的表现。但在 ROCm 环境下,我发现在同样的数据集上,直接使用这套参数会导致 Loss 曲线在初期剧烈震荡,甚至直接发散。
经过多轮对比测试,降低初始学习率是第一个关键动作。对于 Qwen-7B 进行全量微调或高秩 LoRA 时,我将学习率下调至1e-4甚至5e-5,配合 Warmup 比例从默认的 0.03 提升至 0.1,显著平滑了训练初期的梯度更新。这并非因为 AMD 算力不足,而是底层矩阵乘法库(rocBLAS/MIOpen)在混合精度计算时的累积误差特性与 cuBLAS 存在细微差异,较大的步长容易放大这些噪声。
批次大小(Batch Size)的选择也更有讲究。受限于显存管理机制的不同,ROCm 下 PyTorch 的显存碎片化有时比 CUDA 更明显。如果强行塞入过大的 Per Device Train Batch Size,不仅不会提升吞吐,反而可能触发 OOM 或者导致梯度同步延迟。我的经验是:宁可减小单卡 Batch Size,也要保证 Gradient Accumulation Steps 的稳定性。
例如,在单张 MI250 上微调 Qwen-14B,我将per_device_train_batch_size设为 2,gradient_accumulation_steps设为 8,这样既避免了显存峰值尖刺,又保证了有效 Batch Size 足够大以维持收敛方向。以下是一个典型的 LLaMA-Factory 配置片段,针对 ROCm 做了保守优化:
model_name_or_path:Qwen/Qwen1.5-7B-Chatstage:sftdo_train:truefinetuning_type:loralora_target:alltemplate:qwenlearning_rate:5.0e-5num_train_epochs:3.0max_samples:1000per_device_train_batch_size:2gradient_accumulation_steps:8lr_scheduler_type:cosinewarmup_ratio:0.1bf16:true# 强烈建议开启 bf16,fp16 在部分旧版 ROCm 下不稳定ddp_timeout:180000000注意这里强制开启了bf16: true。在较新的 AMD 架构(如 CDNA3)上,BF16 的硬件支持已经非常完善,且相比 FP16 能更好地避免梯度下溢问题,这对 Qwen 这种对精度敏感的模型至关重要。
直面数值精度差异:当 Loss 突然“跳变”
在训练进行到 midway(约 40% 进度)时,我曾多次遇到 Loss 突然从 1.2 跳变到 3.5 的情况,检查发现并非数据脏读,而是典型的数值溢出。这在 CUDA 环境下极少发生,但在 ROCm 的某些算子实现中,特别是在处理 Qwen 特有的 SwiGLU 激活函数或 Rotary Embedding 时,中间变量的动态范围如果超出 FP16 的承受极限,就会引发“雪崩”。
解决这个问题的策略主要有两点:
- 禁用纯 FP16,全面转向 BF16:如上所述,只要硬件支持,务必使用 BFloat16。它在保持与 FP32 相同指数位宽的同时,提供了更大的动态范围,极大地抑制了溢出风险。如果在老款显卡(仅支持 FP32/FP16)上运行,则必须退回到纯 FP32 训练,虽然速度会慢,但能保住模型不废。
- 调整 Clip Grad Norm:默认梯度裁剪值通常是 1.0,但在 ROCm 上微调 Qwen 时,我建议将其收紧至
0.5甚至0.3。这能有效防止个别异常样本产生的巨大梯度破坏整体权重分布。
此外,LLaMA-Factory 的日志监控功能在这里帮了大忙。通过实时观察loss和grad_norm的变化,一旦发现有上升趋势,立即暂停并检查当前的精度设置,往往能挽救一次长达数天的训练任务。
分布式训练中的通信陷阱
单机多卡是常态,但在 ROCm 下启动分布式训练(DDP),经常会卡在初始化阶段,或者在几个 Step 后报RCCL通信超时。这通常是因为网络拓扑识别或环境变量配置不当。
Qwen 模型参数量较大,多卡间梯度同步频繁。确保HCCL_HOME或相关 RCCL 库路径正确指向 ROCm 安装目录是基础。更隐蔽的问题在于可见设备列表。务必在启动脚本前 exportHIP_VISIBLE_DEVICES,而不是沿用 CUDA 的CUDA_VISIBLE_DEVICES。例如:
exportHIP_VISIBLE_DEVICES=0,1,2,3 accelerate launch--config_filedefault_config.yaml src/train.py...如果在容器化环境中,还需要确认 Docker 是否透传了/dev/kfd和/dev/dri设备文件。有一次我遇到的偶发性死锁,最后发现是容器权限限制导致 RCCL 无法正确建立 P2P 连接,降级为 PCIe 通信后虽然带宽下降,但稳定性恢复了。对于追求极致效率的场景,建议查阅主板 BIOS 设置,开启 Above 4G Decoding 和 Re-Size BAR 支持,这对 AMD 多卡互联的性能释放影响巨大。
写在最后:从“能跑”到“好用”
在 ROCm 上微调 Qwen 系列,本质上是一场与细节的博弈。它不再像早期那样连环境都配不平,现在的挑战更多在于如何让模型在异构硬件上表现出与原生环境一致的鲁棒性。通过压低学习率、拥抱 BF16 精度、以及精细化的梯度控制,我们完全可以在 AMD 显卡上获得高质量的微调结果。
这套流程跑通后,最大的感受是“可控”。不再盲目相信默认参数,而是根据硬件特性去调整策略。随着 SGLang 推理端和 LLaMA-Factory 训练端对 ROCm 支持的日益成熟,AMD 平台在大模型落地中的性价比优势正在转化为实实在在的生产力。如果你也在尝试这条路径,不妨从调整那个小小的learning_rate开始,或许会有意想不到的收获。
200小时GPU算力已就位,快来领取:https://marketing.csdn.net/questions/Q2604140858304426315?utm_source=AIpaper