很多朋友手头是AMD显卡,在Windows 10/11上想跑深度学习,第一反应就是上网搜教程,结果搜出来一堆“装WSL2”“换Linux”“上云租GPU”的答案。不是说这些方案不对,而是对一个只想先在本地快速验证模型、跑个YOLO推理、或者做个课程实验的人来说,太重了。我踩过不少坑之后,现在最常用的方案就是Pytorch-DirectML——它能让你在Windows里直接用AMD显卡跑PyTorch,不需要双系统、不需要虚拟机、也不用换卡。这篇文章就专门把这个方案的完整部署过程、验证方法和常见问题讲清楚,照着做就行。
1. 为什么要用Pytorch-DirectML:AMD在Windows下的现实困境
1.1 为什么AMD显卡在Windows下跑深度学习这么费劲
先说一个很多人没搞清楚的背景。PyTorch官方对NVIDIA显卡的支持是走CUDA生态的,CUDA是NVIDIA自己的闭源计算平台,AMD显卡用不了。AMD自己搞了一套ROCm计算平台,性能其实也不错,但官方主打的是Linux系统,Windows版本要么没有、要么是社区魔改版,安装那叫一个折腾。
所以AMD用户在Windows下面临的局面就是:CUDA用不了,ROCm又不给你官方装。于是很多人被迫去装WSL2,在Windows的Linux子系统里跑ROCm,或者在虚拟机里折腾。这当然能解决问题,但环境配置复杂,磁盘占用大,对刚上手的同学非常不友好。
Pytorch-DirectML的出现就是来解决这个尴尬的。它是微软官方维护的一个PyTorch插件,底层走的是DirectX 12里的DirectML接口。DirectX 12是Windows自带的图形API,所有现代显卡、包括AMD、Intel核显、甚至NVIDIA显卡,都很完整地支持。所以通过DirectML,PyTorch就能在Windows上直接调用AMD显卡做计算,不用换系统,也不用碰CUDA。
1.2 DirectML、CUDA、ROCm三者的区别
为了让你对“用了DirectML后到底处于什么水平”有个清晰认知,我做了一个最简单直接的对比表格:
| 方案 | 支持训练 | 支持推理 | Windows官方支持 | 安装难度 | 生态完整度 |
|---|---|---|---|---|---|
| CUDA(NVIDIA) | 支持 | 支持 | 支持 | 简单 | 最高 |
| ROCm(AMD Linux) | 支持 | 支持 | 不官方支持 | 较复杂 | 高 |
| ROCm(AMD Windows) | 支持 | 支持 | 基本没有 | 很折腾 | 低 |
| torch-directml | 基本可用 | 支持 | 官方支持 | 很简单 | 中 |
| ONNX Runtime DirectML | 不支持训练 | 支持 | 官方支持 | 简单 | 中 |
看完这个表你就明白,torch-directml并不是性能最强的方案,但它是“在Windows上最快跑起来”的方案。如果你的需求是:先验证代码能不能跑通、跑个小模型训练、做推理实验,这个方案完全够用。但如果你要微调7B、13B这种大模型,那我还是建议直接租云GPU,别在本地折磨自己。
1.3 torch-directml的能力边界
在开始装之前,得给你打个预防针:torch-directml不是万能的。
它支持PyTorch里大部分常用算子,也支持自动求导,所以训练小型模型是没问题的。但它的算子覆盖度不如CUDA那么全,像某些新出的Transformer层、自定义损失函数、或者一些特殊的数学操作,它可能不支持,会直接报错。另外,torch.compile这种PyTorch 2.0的新特性,在DirectML后端也是不可用的。
还有一个性能问题。DirectML的显存管理和算子融合做得不如CUDA精细,所以在同样一张显卡上,DirectML的实际吞吐可能只有CUDA的一半甚至更低。说句实在话,跑个大点的模型,体感会比较肉。但咱们话说回来,能用就已经解决从0到1的问题了,性能从1到10那是后面慢慢优化的事。
2. 开始之前:驱动、Python环境和版本匹配
2.1 硬件和驱动检查清单
动手安装之前,先把基础条件确认了,别装完环境才发现驱动不对。
第一件事,检查Windows版本。WDDM模式是必须的。你可以按Win+R,输入dxdiag,在弹出的对话框里看“显示”选项卡,里面会列出显卡型号和驱动版本。这里只需要确认一点:Windows 10版本号不低于2004,Windows 11随便什么版本都行。
第二件事,更新AMD显卡驱动。去AMD官网下载Adrenalin版最新驱动,装完以后在任务管理器里看一眼显卡驱动日期,尽量是最近一年之内的。我一再强调驱动,是因为很多人装完torch-directml后报错找不到设备,排查到最后发现是驱动版本太老,DirectML接口根本不认。这种坑,能提前避就提前避。
第三件事,注意核显和独显共存的问题。如果你电脑是AMD核显加AMD独显,或者Intel核显加AMD独显的组合,DirectML会同时识别到两个设备。后面我会讲怎么确认当前用的是哪块卡,这一步千万别跳过,否则你可能辛辛苦苦跑半天,结果全是在核显上跑的,性能比CPU好不了多少。
2.2 创建Python虚拟环境
理论上你直接用系统Python也行,但我强烈建议你用Anaconda或者Miniconda建一个独立环境。为什么?因为深度学习环境里各种包的版本冲突是家常便饭,建独立环境至少能把冲突限定在可控范围。
安装Miniconda的过程就不展开了,装完以后打开Anaconda Prompt,依次执行下面两条命令:
conda create -n dml python=3.10 -y conda activate dmlPython版本强烈建议用3.10。torch-directml官方测试过3.8到3.11,3.10是稳定性最好的中间值。用Python 3.12的话,很多wheel包缺失,你会卡在安装阶段就开始怀疑人生。
2.3 安装PyTorch与torch-directml
进入环境以后,先用国内镜像配置一下pip源,下载速度肉眼可见地提升:
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple然后安装PyTorch。注意,这里是关键一步:不要安装带CUDA后缀的版本,哪怕是NVIDIA用户,用DirectML方案也完全不需要CUDA,装上反而会拖慢启动、占用磁盘空间。执行:
pip install torch --index-url https://download.pytorch.org/whl/cpu这里用官方CPU源,装下来的就是纯CPU版PyTorch,也是后续torch-directml能正常工作的前提条件。接着安装微软的DirectML插件:
pip install torch-directml装完之后可以看一眼版本:
pip show torch torch-directml2.4 版本对应关系别搞错
torch-directml和PyTorch之间有严格的版本绑定。要是你随便装了一个新版的torch,再装torch-directml,导入的时候十有八九会报DLL加载错误之类的问题。目前常见的对应关系大概是这样的:
| torch-directml版本 | 对应PyTorch版本 | 支持Python版本 | 说明 |
|---|---|---|---|
| 0.2.x | 1.13.x | 3.8-3.11 | 老版本,兼容老项目 |
| 0.3.x | 2.0.x | 3.8-3.11 | 推荐,算子覆盖更完整 |
| 更新版本 | 需要看官方说明 | 需确认 | 版本更新后以官方为准 |
最稳妥的方式是:先装好torch,再直接pip install torch-directml,它会自动拉取匹配的插件版本。但如果你遇到导入失败,第一反应应该就是版本不匹配,回到这里查一下对应关系。
3. 三步验证:确认GPU真的被PyTorch用起来了
3.1 读取设备信息
装完环境以后,先别急着跑大模型,第一步是验证PyTorch能不能看到AMD显卡。新建一个Python文件,输入以下代码:
import torch import torch_directml as tdml print(tdml.device_count()) print(tdml.device_name(0)) print(tdml.device(0))device_count()会返回可用GPU数量,device_name(i)则是第i个设备的名称。如果你的电脑只有一张独立显卡,这一行会输出类似“AMD Radeon RX 6600”这样的名称。
如果device_count()返回0,先别慌,大概率是驱动版本问题,或者你正在通过远程桌面连接这台电脑。DirectML在某些情况下依赖本地图形会话,关掉远程桌面,回到物理机上跑一次,经常就好了。
3.2 跑一次矩阵乘法验证
设备识别成功以后,跑一次大的矩阵乘法,这能直观验证GPU计算能力和CPU之间的差距。代码如下:
import torch import torch_directml as tdml import time dml = tdml.device() for name, dev in [("cpu", "cpu"), ("dml", dml)]: a = torch.randn(8192, 8192, device=dev) b = torch.randn(8192, 8192, device=dev) tdml.synchronize() t0 = time.time() c = a @ b tdml.synchronize() print(f"{name}: {time.time() - t0:.4f}s")这里用了tdml.synchronize()来做同步,因为DirectML的算子调用是异步的,不加同步的话计时会不准。如果你的torch-directml版本没有这个方法,那就多跑几轮取最小值,也能大致判断性能。
正常情况下,DML设备跑8192x8192矩阵乘法,应该比CPU快几倍到几十倍。如果你发现DML和CPU速度差不多,甚至更慢,那很可能你的计算并没有真正跑到独显上——赶紧去看下一节的“GPU占用率检查”。
3.3 观察GPU占用率确认是否真的在算
这个检查很多人会忽略。咱不能光听代码不报错,就认定GPU在干活,尤其是有核显加独显并存的环境。
跑上面那个矩阵乘法的时候,同时打开任务管理器,切到“性能”选项卡,看GPU列表。如果GPU占用率能冲到80%以上,说明真的在计算。如果占用率一直在个位数,或者你发现跑代码的时候是核显在飙升而独显纹丝不动,那就是设备选择出了问题。
怎么解决?很简单,打印出所有设备名称,找到独显对应的编号,然后用tdml.device(编号)手动指定。举个例子,如果你的核显是0号,独显是1号,那初始化设备的时候就写:
dml = tdml.device(1)这一步非常关键,直接影响你的后续所有代码。别问我怎么知道的——我第一次跑就盯着核显算了半天,浪费了两天时间才发现自己选错设备了。
4. 动手实操:训练一个MNIST分类器,再用YOLOv8跑目标检测
4.1 用DirectML训练一个完整的MNIST模型
验证完设备,我们跑一个真正的训练任务。MNIST手写数字识别可以说是深度学习界的“Hello World”,用全连接网络训练它,既能验证整套环境,又不会花太长时间。
import torch import torch_directml as tdml from torch import nn from torch.utils.data import DataLoader from torchvision import datasets, transforms dml = tdml.device() transform = transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ]) train_set = datasets.MNIST(root="./data", train=True, download=True, transform=transform) train_loader = DataLoader(train_set, batch_size=64, shuffle=True, num_workers=2) class Net(nn.Module): def __init__(self): super().__init__() self.fc1 = nn.Linear(784, 256) self.fc2 = nn.Linear(256, 128) self.fc3 = nn.Linear(128, 10) self.relu = nn.ReLU() def forward(self, x): x = x.view(x.size(0), -1) x = self.relu(self.fc1(x)) x = self.relu(self.fc2(x)) return self.fc3(x) model = Net().to(dml) optimizer = torch.optim.Adam(model.parameters(), lr=0.001) loss_fn = nn.CrossEntropyLoss() for epoch in range(3): for i, (images, labels) in enumerate(train_loader): images, labels = images.to(dml), labels.to(dml) preds = model(images) loss = loss_fn(preds, labels) optimizer.zero_grad() loss.backward() optimizer.step() if i % 100 == 0: print(f"epoch {epoch}, step {i}, loss {loss.item():.4f}")几个要点说一下。所有张量都要用.to(dml)搬到GPU上,包括输入图片、标签、模型参数,这一步漏了的话,计算会部分回退到CPU,速度立刻打回原形。第二点,模型尽量用全连接和卷积这些经典结构,这类算子在DirectML上覆盖很齐全,训练过程基本不会报错。第三点,num_workers这个参数在这里是可以正常使用的,适当调大能缓解数据加载瓶颈,但别超过8,Windows上开太多worker反而容易出问题。
在我的一张RX 6600上,跑完3个epoch大约十分钟出头,考虑到MNIST数据量不大,这个速度完全在可接受范围内。
4.2 用YOLOv8做目标检测推理
训练验证完以后,很多人关心的就是实际应用了。YOLO是目标检测领域使用率最高的模型,这里直接用Ultralytics提供的YOLOv8n来演示推理。
先安装Ultralytics:
pip install ultralytics然后写推理代码:
import torch from ultralytics import YOLO import torch_directml dml = torch_directml.device() model = YOLO("yolov8n.pt") results = model.predict(source="bus.jpg", device=dml) results[0].show()这里有个兼容性细节值得说一下。不同版本的Ultralytics对device参数的处理有差异,有的版本直接传torch.device对象就行,有的版本需要传字符串。如果你遇到“Invalid device”之类的报错,可以改成先手动把模型搬过去:
model.model.to(dml) results = model.predict(source="bus.jpg")第一次运行的时候,Ultralytics会自动下载yolov8n.pt权重文件,大概6MB左右,耐心等一会儿就行。推理速度方面,在RX 6600上处理一张普通照片基本在几十毫秒级别,体感和入门级NVIDIA显卡差距不大,日常使用完全够了。
4.3 部署场景:导出ONNX后用DirectML加速
如果你只是想本地验证,上面的步骤已经够用了。但如果是部署到生产环节,我不建议直接拿torch-directml跑,因为它依赖PyTorch整个框架,打包体积大、启动慢。更推荐的做法是:先把PyTorch模型导出成ONNX格式,然后用ONNX Runtime的DirectML执行引擎来跑推理。
导出ONNX的代码很简单:
import torch from ultralytics import YOLO import torch_directml dml = torch_directml.device() model = YOLO("yolov8n.pt") model.model.to(dml) dummy_input = torch.randn(1, 3, 640, 640, device=dml) torch.onnx.export( model.model, dummy_input, "yolov8n.onnx", opset_version=12, input_names=["images"], output_names=["output"] )导出成功后,安装onnxruntime-directml:
pip install onnxruntime-directml然后就能用更轻量的方式来做推理。这也是目前很多AI绘画、OCR工具能在Windows上调用AMD显卡加速的底层方案。相比torch-directml,ONNX Runtime DirectML做了更多算子级优化,而且和图形驱动之间的配合更成熟,稳定性也好不少。
5. 我踩过的那些坑:常见报错与性能优化
5.1 设备识别失败的几种情况
用这方案最闹心的就是设备识别不到。我把遇到过的几种情况整理成了下面的速查表:
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
| device_count()返回0 | 显卡驱动太老 | 更新到AMD官网最新驱动 |
| device_count()返回0 | 通过远程桌面连接 | 切回物理桌面再测试 |
| device_count()返回0 | Windows版本过旧 | 升级到Windows 10 2004以上 |
| 跑起来独显占用率为0 | 核显独显并存时选错设备 | 打印device_name列表,手动tdml.device(1) |
| 能跑但速度极慢 | 部分算子回退到CPU | 检查GPU占用率,模型和输入全部放dml设备 |
| import torch_directml报DLL错误 | torch版本与插件版本不匹配 | 卸载重装,按对应关系装匹配版本 |
这里最值得强调的还是驱动问题。AMD的驱动更新频率不算快,有时候Windows推送的驱动更新反而会回退版本。如果你确认代码没问题,建议直接去AMD官网手动下载最新驱动,比系统自动更新靠谱得多。
5.2 算子不支持怎么处理
用DirectML跑一些比较新的模型时,偶尔会遇到类似“DML does not support”的报错。看到这个报错不要慌,这基本是算子覆盖问题,有几个常用的处理思路。
第一个思路是换实现方式。举个例子,如果你的自定义Loss里面用了某个不支持的数学函数,可以手写一个等价但用基础算子拼出来的版本。基础的四则运算、指数、对数这些操作,DirectML覆盖得很全。
第二个思路是把不支持的部分留在CPU上算。虽然性能差点,但至少能跑。你得把整个流程拆开,模型的主体放在dml设备上,只在特定的某个步骤把张量搬回CPU,算出结果再搬到GPU继续。
第三个思路也很实际:换一个兼容性更好的模型结构。你在GitHub上看到有些新算法模型,作者只在NVIDIA显卡上测试过,完全没考虑别的后端。这种模型在DirectML上报错非常正常,要么等社区适配,要么换个经典模型用。
最后,定期更新torch-directml。微软还在持续维护这个插件,新版会补不少算子支持。升级之前建议先看看PyPI上的更新日志,不用每次更新都追,但隔半年升一次还是有必要的。
5.3 性能提不上去的排查思路
好多人在本地跑通了环境,但对速度不满意,跑来问我说DirectML是不是就这个水平。说实话,性能瓶颈很多时候不是DirectML本身的问题,而是数据搬移和程序结构造成的。
第一个非常常见的问题是数据每轮都在CPU和GPU之间来回搬。比如训练循环里每次都把图片先搬到GPU,算完loss又搬到CPU,再搬回去。这种频繁的同步会严重拖慢速度。正确做法是让整个计算链条都留在GPU上,尽量避免中途用.item()把数值取回Python端,打印loss这种操作只有在调试时才做。
第二个问题是数据加载跟不上。GPU算得太快,CPU准备数据忙不过来,GPU就在那干等。解决方法是把DataLoader的num_workers调大,并且设置pin_memory=True。虽然pinned memory在DirectML上不是所有场景都有效,但开了一般不会更差。
第三个问题是你可能在毫不知情下使用了CPU回退。有个简单办法:跑推理的时候保持GPU占用率监控窗口打开,如果GPU占用率一直在10%以下徘徊,说明大部分算子在CPU上执行,检查一下代码里是不是有变量忘加.to(dml)。
还有个容易忽略的点是混合精度。DirectML对自动混合精度的支持不完整,不建议用torch.cuda.amp。虽然理论上低精度能加速,但在DirectML后端强行用半精度训练,大概率会收获一堆报错。老老实实用float32跑,省心得多。
从我自己的实际体验来说,Pytorch-DirectML这套方案最适合的使用场景就是:想在Windows下低成本验证深度学习代码、跑课程作业、做做目标检测推理实验。它的安装复杂度比WSL2低一个量级,日常使用的稳定性也足够。如果你后续确定要在本地深度学习这条路上长期走下去,再考虑Linux加ROCm那套方案不迟。至少现在,它已经帮我把AMD这张卡在Windows下真正的价值发挥出来了。