不用买卡也能玩转云端 AI:我把整套训练流搬上了 AMD GPU
最近干了一件事: 把一套原本跑在 NVIDIA A100 上的训练和推理代码,整个儿搬到了 AMD Instinct™ MI300X + ROCm™ 上。
说实话,动工之前我也犹豫过——毕竟"换平台"听着就像给自己找麻烦。但真走完一遍才发现,没有想象中那么折腾。这篇就把过程里的坑和经验摊开讲讲,希望你看完能少绕点弯路。文末我放了个 200 小时免费云算力 的领取入口,看完正好拿去实测。

一、先放下一个老偏见
不少人一提 ROCm,第一反应还是"生态弱、文档少、容易踩雷"。这话放在几年前没毛病,但以 ROCm 6.x 为节点,情况真不一样了:
-
PyTorch 官方已经发了 ROCm 原生版本,不是社区魔改的那种;
-
vLLM、SGLang、TGI 这些主流推理框架,基本都适配了;
-
MI300X 单卡就有 192GB 显存,跑 70B 甚至更大的模型,几乎不用纠结怎么切分。
所以这次迁移,我给自己定了个最关键的心法:
把 "CUDA" 当成一个可以换的后端,而不是绑死的运行时。
PyTorch 早就帮你把这层抽象掉了。真正要动手改的地方,主要是环境和个别底层算子,其余的代码基本不用动。
二、第一步:换镜像,别跟驱动死磕
我的第一条建议——千万别在宿主机上手动装驱动。装到怀疑人生不说,版本一冲突能查半天。直接用官方 Docker 镜像,干净利落:
# ROCm 6.2 + PyTorch 2.4 官方镜像 docker pull rocm/pytorch:rocm6.2_ubuntu22.04_py3.11_pytorch_release-2.4.0 docker run -it --device=/dev/kfd --device=/dev/dri \ --group-add=video --ipc=host --cap-add=SYS_PTRACE \ -v $(pwd):/workspace rocm/pytorch:rocm6.2_ubuntu22.04_py3.11_pytorch_release-2.4.0
进容器后,先做个最简单的"冒烟测试",确认显卡被认出来了:
import torch print(torch.__version__) # 2.4.0+rocm62 print(torch.cuda.is_available()) # True —— 没看错,ROCm 下也是 True print(torch.cuda.device_count()) # 你的卡数 print(torch.cuda.get_device_name(0)) # AMD Instinct MI300X
这里有个反直觉、但故意这么设计的点: ROCm 版的 PyTorch 保留了 torch.cuda 这个名字。也就是说,你以前写的成千上万行 x.cuda()、model.to('cuda'),一行都不用改——cuda 在这里其实就是 AMD 显卡(HIP 设备)的别名。
光这一点,就帮你省下了大把改代码的时间。
三、监控命令也得换一套
原来天天敲的 nvidia-smi,到 AMD 这边要换成 rocm-smi。我做了张对照表,建议直接存下来:
| 你想干啥 | NVIDIA | AMD / ROCm |
|---|---|---|
| 看显卡和显存 | nvidia-smi |
rocm-smi |
| 持续刷新 | watch -n1 nvidia-smi |
rocm-smi --watch |
| 看进程占用 | nvidia-smi pmon |
rocm-smi --showpid |
| 看多卡拓扑 | nvidia-smi topo -m |
rocm-smi --showtopo |
| 详细诊断 | nvidia-smi -q |
rocm-smi --showall |
记不住没关系,关键盯住两个数: VRAM%(显存占比) 和 MEM-USE(实际显存),对应的就是以前 nvidia-smi 里的 Memory-Usage。
四、训练代码: 九成情况直接能跑
举个最常见的分布式训练例子。原来的 CUDA 写法长这样:
model = MyModel().cuda() model = torch.nn.parallel.DistributedDataParallel( model, device_ids=[local_rank] )
搬到 ROCm 上,原样跑就行。要留意的只有两个小细节:
一是启动方式。 torchrun 直接能用;但如果你以前依赖的是 apex 做分布式,建议换成原生的 DDP + torchrun,因为 apex 在 ROCm 上支持不太好。
二是别纠结逐位数值对齐。 换了平台,浮点累加顺序变了,单个 tensor 的数值会有微小差异。验收的时候别去逐个比对,看最终的 loss 和 accuracy 曲线是不是在一条收敛轨迹上就行。
至于偶尔冒出来的 "operator not implemented" 报错,别慌——大概率是下面第五节要讲的情况。
五、真正要改代码的地方: 自定义算子
前面几节都在说"基本不用改",那到底什么时候必须动手?答案是:你自己写的 CUDA Kernel。
好消息是,迁移规律特别机械,几乎就是一套"查找替换":
// 原来(CUDA) #include <ATen/cuda/CUDAContext.h> at::cuda::CUDAGuard guard(device); my_kernel<<<grid, block, 0, at::cuda::getCurrentCUDAStream()>>>(x, y, n);
// 现在(ROCm / HIP) #include <ATen/hip/HIPContext.h> at::hip::HIPGuard guard(device); my_kernel<<<grid, block, 0, at::hip::getCurrentHIPStream()>>>(x, y, n);
说白了就是把名字里的 cuda 换成 hip:
| CUDA | HIP / ROCm |
|---|---|
cuda |
hip |
ATen/cuda/ |
ATen/hip/ |
CUDAGuard |
HIPGuard |
cudaMalloc |
hipMalloc |
cudaMemcpy |
hipMemcpy |
懒得一个个换?有两个偷懒办法。
第一个,写段宏,让同一份代码在两边都能编译:
#ifdef __HIP_PLATFORM_AMD__ #define CUDA_PREFIX(name) hip##name #else #define CUDA_PREFIX(name) cuda##name #endif
第二个更省事——AMD 自带一个 hipify-perl 工具,一条命令就能把整个文件批量转好:
hipify-perl my_kernel.cu > my_kernel.hip
六、推理更省心: 直接上 vLLM
训练搞定了,推理这块反而更简单。vLLM 在 ROCm 上已经是"一等公民"级别的支持,一条命令就能把服务拉起来:
# 单卡跑 Qwen2.5-72B,MI300X 显存完全装得下 vllm serve Qwen/Qwen2.5-72B-Instruct \ --tensor-parallel-size 1 \ --gpu-memory-utilization 0.9 \ --max-model-len 32768
调优的时候,我主要盯这三个地方:
-
显存利用率可以大胆调高,MI300X 显存大,给到 0.9 甚至 0.95 都没问题;
-
偶尔遇到 attention 算子不稳定,试试关掉 triton 版本的 FA(设
VLLM_USE_TRITON_FLASH_ATTN=0),换成 CK(Composable Kernel) 通常更稳; -
跑长文本时,先开
--enforce-eager建一条"基准线",再逐步打开图优化。这样万一性能退化了,你能一眼定位是哪步的锅。
七、迁移完不算完,得拿数据说话
搬到新平台,不能凭感觉说"差不多了",得摆数据。我一般会整理这么一张对比表(同等 batch、同代显卡):
| 指标 | A100 (CUDA) | MI300X (ROCm) | 怎么看 |
|---|---|---|---|
| 训练吞吐(samples/s) | 基准 | +x% | 看显存吃满后的步进 |
| 推理吞吐(tok/s) | 基准 | +x% | 重点看长文本的下限 |
| 显存占用 | 基准 | -x% | MI300X 大显存的主场 |
| 冷启动时间 | 基准 | ≈ | 框架加载,差距通常很小 |
这里有个特别常见的坑: 第一次跑发现 ROCm 比 CUDA 慢,立马下结论"AMD 不行"。其实很多时候,慢只是因为第一次在编译 kernel,第二次开始就正常了。所以计时之前,一定记得先 warm-up 跑几轮。
八、报错速查表(收藏备用)
整理了几类高频报错,建议存着,遇到了直接对号入座:
| 报错关键字 | 啥原因 | 怎么解决 |
|---|---|---|
MIOpen(HIP) 相关 |
卷积 kernel 没找到 | 设 MIOPEN_FIND_MODE=3,走启发式 |
hipErrorNoBinaryForGpu |
镜像和显卡架构对不上 | 按卡型加 HSA_OVERRIDE_GFX_VERSION=9.4.2 |
peer access is not available |
多卡之间没法直连 | 检查 --device=/dev/kfd 和 IOMMU 设置 |
显存报 OOM,但 rocm-smi 显示没用满 |
显存碎片 / PyTorch 缓存 | 设 PYTORCH_HIP_ALLOC_CONF=garbage_collection_threshold:0.6 |
九、写在最后
整条路走下来,最大的感受是: 真正要改代码的部分,可能还不到 5%。剩下 95%,都是"换换环境、换个思路"的事。
ROCm 在 6.x 之后,确实跨过了"能用"这道坎。再加上 Instinct 那块大得惊人的显存,对长文本、大模型的推理场景来说,吸引力是实打实的。
如果你也想试试,其实不用急着去买卡——AMD 开发者云直接就能开 Instinct 实例,这套脚本原样丢上去就能跑。
🚀 想动手?先领 200 小时免费云算力
别让"没有硬件"成了你动手的借口。
现在 AMD AI 开发者计划开放注册,注册就送 200 小时免费云算力,AMD Instinct™ GPU + ROCm™ 全栈环境开箱即用,不用自己配机器、装驱动、查冲突。
👉 点这里,加入计划并领取 200 小时免费云算力: https://s.csdn.cn/ik9E3m
把这篇文章里的 Docker 镜像、迁移脚本、vLLM 命令原样搬上去,今天就能跑通你的第一个 ROCm 任务。试过之后,欢迎回来聊聊你的实测数据 🚀
更多推荐


所有评论(0)