WSL2 部署 CUDA 与 PyTorch:GPU 加速深度学习环境搭建实战
1. 为什么我最终选择了 WSL2 而不是双系统或纯 Linux 主机
1.1 一个折腾过三台机器的人的真心话
先说结论:如果你主力工作流在 Windows 上,但又需要 Linux 内核级别的开发体验,同时还要跑 GPU 加速的深度学习任务,WSL2 是目前最省心的方案,没有之一。
我最早是纯 Windows + 虚拟机跑 Ubuntu,后来换成双系统,再后来咬咬牙装了一台纯 Linux 主机。三套方案我都深度用过至少半年以上,踩过的坑足够写一本小册子。虚拟机的 GPU 直通基本是残废状态,NVIDIA 驱动在 VMware 和 VirtualBox 里的表现堪称灾难;双系统切换成本太高,写代码在 Linux、查资料在 Windows,来回重启能把人逼疯;纯 Linux 主机倒是干净,但日常办公、会议软件、某些只有 Windows 才有的工具链又成了新问题。
WSL2 的出现基本终结了这种纠结。它本质上是跑在 Hyper-V 轻量级虚拟化层上的真实 Linux 内核,不是模拟层,不是兼容层,是货真价实的 Linux kernel。你可以用uname -r看到类似5.15.x-microsoft-standard-WSL2的内核版本号,这意味着绝大多数 Linux 原生工具、系统调用、内核模块都能正常工作。更关键的是,从 WSL2 开始,微软和 NVIDIA 合作实现了 GPU 半虚拟化直通,/dev/dxg设备节点让你在 WSL2 里直接调用宿主机的 NVIDIA 显卡,CUDA 和 PyTorch 都能满速跑。
这套方案适合谁?我总结下来是三类人:一是 Windows 生态重度用户但需要 Linux 开发环境的后端/嵌入式工程师;二是搞深度学习需要 CUDA 但不想放弃 Windows 日常体验的算法工程师;三是学生党,只有一台笔记本,既要上课用 Windows 又要做 Linux 实验。如果你属于这三类中的任何一类,接下来的内容值得你花二十分钟看完。
1.2 WSL2 的架构到底和虚拟机差在哪
很多人把 WSL2 当成"微软牌的 VMware",这个理解偏差很大。传统虚拟机是完整的硬件模拟加独立内核,资源开销大,GPU 直通需要 IOMMU 分组和 VFIO 绑定,配置复杂到劝退。WSL2 用的是 Hyper-V 的轻量级 utility VM,内核是微软专门编译的 Linux 内核,通过 VMBus 高速通道和宿主机通信,文件系统通过 9P 协议(现在新版用 virtio-9p 和更快的 virtiofs)挂载。
GPU 这块是重点。WSL2 的 GPU 直通走的是 GPU-PV(GPU Paravirtualization)技术,宿主机上的 NVIDIA 驱动暴露一个/dev/dxg设备给 WSL2 实例,WSL2 内部再通过dxgkrnl内核模块和用户态的libdxcore.so把 CUDA 调用转发到宿主机驱动。所以你不需要在 WSL2 里装完整的 NVIDIA 显卡驱动,只需要装 CUDA Toolkit 里的用户态库就行。这一点和传统虚拟机完全不同,也是 WSL2 能跑满 GPU 性能的根本原因。
实测数据给你一个参考:我在 RTX 4070 Ti 上跑 ResNet-50 的训练 benchmark,WSL2 下的吞吐量大约是纯 Linux 主机的 95% 到 98%,损失主要来自少量内核态转发开销。而 VMware 虚拟机就算勉强配好 GPU 直通,性能也要打七折。这个差距在跑大模型微调的时候会被放大,因为显存带宽和 kernel launch 频率都是瓶颈。
2. 部署前的环境准备与版本选择
2.1 Windows 版本和硬件的前置检查
动手之前先把地基打牢,不然中途报错会让你怀疑人生。WSL2 对 Windows 版本有硬性要求:Windows 10 版本 2004 及以上(内部版本 19041+),或者 Windows 11 任意版本。我强烈建议直接上 Windows 11,因为 WSL2 的很多新特性比如 systemd 支持、镜像网络模式、GPU 直通优化都是 Win11 优先推送的。
检查方法很简单,按Win + R输入winver,看版本号。如果低于 19041,先去 Windows Update 把系统升到位。另外确认 CPU 支持虚拟化,任务管理器里"性能"标签页看 CPU,右下角"虚拟化"必须是"已启用"。如果显示已禁用,进 BIOS 打开 Intel VT-x 或 AMD-V。
还有一个容易被忽略的点:WSL2 需要开启"虚拟机平台"和"适用于 Linux 的 Windows 子系统"两个 Windows 功能。用管理员权限打开 PowerShell,执行:
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart执行完重启一次。这两条命令我见过太多人跳过,结果wsl --install报"WSL2 尚未准备就绪",回头排查半天。
2.2 显卡驱动的正确安装姿势
这是整个部署里最关键也最容易翻车的一步。WSL2 的 GPU 直通要求宿主机装的是 NVIDIA 的Windows 版驱动,而且必须是支持 WSL 的版本(一般 470 以上都行,建议直接上最新 Game Ready 或 Studio 驱动)。注意,你不需要在 WSL2 里再装一遍 Linux 显卡驱动,装了反而会冲突。
去 NVIDIA 官网下载对应你显卡型号的 Windows 驱动,安装时选"自定义安装",勾选"执行清洁安装"。装完在 Windows 命令行里跑nvidia-smi,能看到显卡信息就说明宿主机驱动 OK 了。
然后在 WSL2 里也跑一次nvidia-smi。如果这一步能输出和 Windows 下类似的表格,恭喜你,GPU 直通已经通了。如果报command not found,说明 WSL2 里没装 CUDA 用户态工具,下一步解决。如果报No devices were found,八成是宿主机驱动版本太老或者没开虚拟化,回去检查。
提示:AMD 显卡用户注意,WSL2 的 GPU 直通目前对 AMD 的支持远不如 NVIDIA 成熟,ROCm 在 WSL2 下的可用性有限。如果你主力是 AMD 卡跑深度学习,建议还是考虑纯 Linux 或者云端 GPU 租用方案。
2.3 发行版选择:Ubuntu 22.04 还是别的
WSL2 支持一大堆发行版,Ubuntu、Debian、Kali、openSUSE、AlmaLinux 都有。做 AI 开发我无脑推荐Ubuntu 22.04 LTS,原因有三:一是 NVIDIA 官方 CUDA 文档默认以 Ubuntu 为参考平台,踩坑最少;二是 PyTorch 和 TensorFlow 的官方 wheel 对 Ubuntu 22.04 的 glibc 版本适配最好;三是社区资料最多,遇到问题一搜就有答案。
安装命令一行搞定:
wsl --install -d Ubuntu-22.04装完设置用户名密码,然后sudo apt update && sudo apt upgrade -y把系统更新到最新。这里有个小技巧:先把 apt 源换成国内镜像(清华或阿里),下载速度能从几百 KB 飙到几十 MB。编辑/etc/apt/sources.list,把archive.ubuntu.com和security.ubuntu.com替换成mirrors.tuna.tsinghua.edu.cn即可。
3. CUDA 与 PyTorch 环境的完整搭建
3.1 CUDA Toolkit 的安装与版本匹配逻辑
CUDA 版本选择不是越新越好,要和你的 PyTorch 版本、显卡驱动版本三者匹配。逻辑是这样的:宿主机 NVIDIA 驱动版本决定了支持的 CUDA 最高版本(驱动向下兼容),PyTorch 官方 wheel 又只针对特定 CUDA 版本编译。所以正确顺序是:先看驱动支持到哪个 CUDA,再选 PyTorch 支持的 CUDA 版本,最后装对应的 CUDA Toolkit。
在 WSL2 里装 CUDA Toolkit,不要用apt install nvidia-cuda-toolkit,那个版本又老又不带 WSL 适配。正确做法是去 NVIDIA 官网的 CUDA Toolkit 下载页,选 Linux > x86_64 > WSL-Ubuntu > 2.0 > deb(local),会给你一串安装命令。以 CUDA 12.1 为例:
wget https://developer.download.nvidia.com/compute/cuda/repos/wsl-ubuntu/x86_64/cuda-keyring_1.1-1_all.deb sudo dpkg -i cuda-keyring_1.1-1_all.deb sudo apt-get update sudo apt-get -y install cuda-toolkit-12-1装完把 CUDA 加到环境变量,编辑~/.bashrc:
export PATH=/usr/local/cuda-12.1/bin:$PATH export LD_LIBRARY_PATH=/usr/local/cuda-12.1/lib64:$LD_LIBRARY_PATHsource ~/.bashrc之后跑nvcc -V,能看到版本号就成功了。这时候再跑nvidia-smi,应该也能正常输出。
3.2 PyTorch 安装:别用 pip 默认源
PyTorch 安装最大的坑就是直接pip install torch,这样装的是 CPU 版本,跑起来发现torch.cuda.is_available()返回 False,白折腾。正确姿势是去 PyTorch 官网首页的安装选择器,选好你的 CUDA 版本,它会给你对应的命令。以 CUDA 12.1 为例:
pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121如果你在国内,download.pytorch.org的速度可能很感人。可以换成清华的 PyTorch 镜像源:
pip3 install torch torchvision torchaudio -i https://pypi.tuna.tsinghua.edu.cn/simple但注意清华源不一定有最新的 CUDA 版本 wheel,如果装完发现 CUDA 不可用,还是老老实实用官方源,挂个代理或者耐心等。
验证安装是否成功,跑这段 Python:
import torch print(torch.__version__) print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0)) print(torch.cuda.get_device_capability(0))如果is_available()返回 True,get_device_name输出你的显卡型号,那就大功告成了。get_device_capability返回的是计算能力,比如 RTX 40 系是 (8, 9),这个值在编译自定义 CUDA 算子的时候会用到。
3.3 用 Conda 管理多套环境
做 AI 开发不可能只用一个环境,不同项目依赖的 PyTorch 版本、CUDA 版本经常打架。我强烈建议用 Miniconda 而不是系统 Python,隔离性更好。安装:
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh bash Miniconda3-latest-Linux-x86_64.sh一路回车,最后conda init bash,重开终端。然后创建环境:
conda create -n pytorch-dev python=3.10 -y conda activate pytorch-dev这里有个很多人踩的坑:在 WSL2 里conda activate报"无法将 conda 项识别",这是因为 conda 的初始化脚本没被 source。解决方法是source ~/miniconda3/etc/profile.d/conda.sh,或者干脆重开一个终端。如果还不行,检查~/.bashrc里有没有 conda 的 init 块。
4. 性能调优与常见问题排查
4.1 文件系统性能:别在 /mnt/c 下跑训练
这是 WSL2 性能最大的坑,没有之一。WSL2 访问 Windows 文件系统(/mnt/c/...)走的是 9P 协议,IO 性能极差,小文件读写能慢十倍以上。如果你把数据集和代码放在/mnt/c/Users/xxx/project下跑训练,GPU 利用率会被 IO 拖到 30% 以下,显卡在那干等着读数据。
正确做法是把项目放在 WSL2 的原生文件系统里,也就是~/project或者/home/username/...。从 Windows 访问这些文件可以通过\\wsl$\Ubuntu-22.04\home\username这个路径,资源管理器直接输入就行。数据集如果太大不想复制两份,可以考虑用符号链接,但性能还是不如原生。
我实测过一个对比:同样一个 10GB 的 ImageNet 子集,放在/mnt/c下训练一个 epoch 要 8 分钟,放在~/data下只要 3 分半。这个差距在迭代调参的时候是致命的。
4.2 显存和内存的分配策略
WSL2 默认会占用宿主机最多 50% 的内存,这个值可以在C:\Users\你的用户名\.wslconfig里调整。如果宿主机 32GB 内存,WSL2 默认最多用 16GB,跑大模型可能不够。新建或编辑这个文件:
[wsl2] memory=24GB processors=8 swap=8GB localhostForwarding=true改完wsl --shutdown重启 WSL2 生效。注意 memory 不要设得和宿主机总内存一样,要给 Windows 留至少 4GB,不然宿主机自己会卡。
显存方面 WSL2 是动态分配的,用多少占多少,不需要手动配置。但有个坑:如果训练进程异常退出,显存可能不会立即释放,nvidia-smi里看到残留进程。这时候wsl --shutdown再重进是最干净的解法。
4.3 常见报错速查表
我把这几年遇到的高频问题整理成表,方便你对号入座:
| 报错信息 | 根本原因 | 解决方案 |
|---|---|---|
WSL2 尚未准备就绪 | 虚拟机平台功能未开启 | 管理员 PowerShell 执行 dism 开启功能并重启 |
nvidia-smi: command not found | WSL2 内未装 CUDA 用户态工具 | 安装对应版本 CUDA Toolkit |
No devices were found | 宿主机驱动不支持 WSL 或版本过老 | 更新 NVIDIA Windows 驱动到最新 |
torch.cuda.is_available() = False | 装了 CPU 版 PyTorch | 用官方 CUDA 索引重装 PyTorch |
CUDA out of memory | 显存不足或残留进程 | 减小 batch size,wsl --shutdown清理 |
conda: command not found | conda 未初始化 | conda init bash后重开终端 |
| 训练速度异常慢 | 数据在 /mnt/c 下 | 迁移到 WSL2 原生文件系统 |
libcudart.so not found | LD_LIBRARY_PATH 未配置 | 在 .bashrc 里加 CUDA lib 路径 |
4.4 几个提升体验的实用技巧
第一个技巧是配置 Windows Terminal。Win11 自带的 Windows Terminal 对 WSL2 支持极好,可以设置字体、配色、快捷键,还能开多个标签页同时跑不同发行版。在设置里把默认配置文件设为 Ubuntu-22.04,启动就直达。
第二个技巧是 SSH 远程连接。虽然 WSL2 就在本机,但用 VS Code 的 Remote-WSL 插件开发体验比在终端里 vim 好太多。装好插件后,在 WSL2 项目目录下敲code .,VS Code 会自动连上 WSL2 环境,Python 解释器、调试器、Jupyter 全都能用,而且用的是 WSL2 里的 Python 和 CUDA。
第三个技巧是定期备份。WSL2 的发行版可以用wsl --export Ubuntu-22.04 backup.tar导出成 tar 包,换机器或者搞崩了直接wsl --import恢复。我一般配好环境就导出一份,省得重装。导出文件可能几十 GB,找个空间大的盘放。
第四个技巧是关于systemd。新版 WSL2 支持 systemd,在/etc/wsl.conf里加:
[boot] systemd=true这样一些依赖 systemd 的服务(比如 docker)就能正常跑。不过开了 systemd 启动会慢一点,看你需求。
5. 从零到跑通第一个 GPU 训练任务
5.1 环境验证的完整流程
环境搭好之后别急着上大项目,先用一个最小例子验证全链路。我习惯用 MNIST 加一个简单 CNN,五分钟能跑完,能验证 CUDA、cuDNN、PyTorch 三者是否协同工作。
import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms device = torch.device("cuda" if torch.cuda.is_available() else "cpu") print(f"Using device: {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 = torch.utils.data.DataLoader(train_set, batch_size=128, shuffle=True, num_workers=4) class Net(nn.Module): def __init__(self): super().__init__() self.conv1 = nn.Conv2d(1, 32, 3, 1) self.conv2 = nn.Conv2d(32, 64, 3, 1) self.fc1 = nn.Linear(9216, 128) self.fc2 = nn.Linear(128, 10) def forward(self, x): x = torch.relu(self.conv1(x)) x = torch.relu(self.conv2(x)) x = torch.flatten(x, 1) x = torch.relu(self.fc1(x)) return self.fc2(x) model = Net().to(device) optimizer = optim.Adam(model.parameters(), lr=1e-3) criterion = nn.CrossEntropyLoss() for epoch in range(3): for batch_idx, (data, target) in enumerate(train_loader): data, target = data.to(device), target.to(device) optimizer.zero_grad() output = model(data) loss = criterion(output, target) loss.backward() optimizer.step() if batch_idx % 100 == 0: print(f"Epoch {epoch} Batch {batch_idx} Loss {loss.item():.4f}")跑起来之后开另一个终端watch -n 1 nvidia-smi,能看到 GPU 利用率在波动、显存被占用,就说明整条链路通了。如果 GPU 利用率一直是 0,检查num_workers是不是设太大导致 CPU 瓶颈,或者数据是不是在/mnt/c下。
5.2 大模型微调场景的显存估算
跑通小例子之后,很多人会直接上大模型微调,然后被 OOM 教做人。显存估算有个经验公式:模型参数量 × 精度字节数 × 系数。全精度(FP32)训练系数约 4(参数+梯度+优化器状态+激活值),混合精度(FP16/BF16)约 2,LoRA 微调约 0.5 到 1。
举个例子,7B 参数的模型,FP16 全量微调大概需要 7B × 2 × 2 = 28GB 显存,加上激活值实际要 40GB 以上,单张 24GB 卡根本放不下。这时候要么上 LoRA(显存降到 10GB 左右),要么用 DeepSpeed ZeRO 做参数分片,要么量化到 4bit(QLoRA)。
WSL2 下这些方案都能跑,和纯 Linux 没区别。唯一要注意的是多卡场景,WSL2 目前对多 GPU 直通的支持还在完善中,单卡是最稳的。如果你有多卡需求,建议还是纯 Linux 主机。
5.3 数据加载的优化细节
GPU 训练最怕的就是 GPU 等数据。除了前面说的把数据放原生文件系统,还有几个优化点。DataLoader的num_workers设置很关键,一般设为 CPU 物理核心数,比如 8 核 CPU 设 8。设太大反而会因为进程切换开销变慢。pin_memory=True能加速 CPU 到 GPU 的数据传输,配合non_blocking=True效果更好。
如果数据集是小文件(比如几万张图片),可以考虑打包成 LMDB 或者 WebDataset 格式,减少文件系统元数据开销。我做过一个项目,原始 5 万张小图,每个 epoch 数据加载要 40 秒,打包成 LMDB 后降到 8 秒,GPU 利用率从 60% 提到 95%。
另外torch.compile在 WSL2 下也能用,PyTorch 2.0 之后的新特性,能把模型编译成更高效的 kernel,训练速度提升 20% 到 30%。不过首次编译要等几分钟,适合长时间训练的场景。
6. 关于这套方案的一些个人体会
折腾 WSL2 这几年,我最大的感受是它把"Windows 办公 + Linux 开发 + GPU 计算"这三个原本割裂的场景缝到了一起。以前我要么忍受虚拟机的性能损失,要么忍受双系统的切换成本,现在一个 Windows Terminal 标签页就全搞定了。
当然它也不是银弹。多卡训练、需要特定内核模块的场景、对 IO 延迟极度敏感的任务,WSL2 还是不如纯 Linux。但对 90% 的个人开发者和中小团队来说,这套方案的性价比是最高的。省下来的时间拿去调模型、读论文,比折腾系统配置有价值得多。
最后分享一个我踩过的坑:有次训练跑了一整夜,第二天发现 WSL2 被 Windows 自动更新重启了,训练全白费。后来我在.wslconfig里加了vmIdleTimeout=-1,并且在 Windows Update 里把活动时间设长,再也没出过这问题。跑长任务之前,记得检查一下 Windows 的自动重启设置,这个细节能救你一命。