1. 为什么要在 Windows 上折腾 WSL2 而不是双系统
很多人第一次接触深度学习开发时,都会面临一个经典的选择题:到底是在 Windows 上直接跑,还是装个 Ubuntu 双系统,或者干脆买台 Linux 服务器。我前后试过这三种方案,最后稳定下来的组合是 Windows 11 + WSL2 + 独立显卡直通。这套方案最大的好处是,你不需要为了跑一个 PyTorch 训练脚本就重启电脑切系统,也不用忍受虚拟机的性能损耗。
WSL2 和早期的 WSL1 有本质区别。WSL1 是微软做的一套系统调用翻译层,它把 Linux 的系统调用实时翻译成 Windows 的调用,文件系统性能差、兼容性也一般。WSL2 则直接跑了一个轻量级的完整 Linux 内核,通过 Hyper-V 的虚拟化能力运行,本质上是一台虚拟机,但微软把它做得足够轻、足够透明,你在终端里的体验和原生 Linux 几乎没有差别。更关键的是,从 Windows 11 的某个版本开始,WSL2 支持了 GPU 半虚拟化,也就是把宿主机的显卡以直通的方式暴露给 Linux 子系统,CUDA 和 DirectML 都能直接用。
这意味着什么?意味着你可以在 WSL2 里装 CUDA Toolkit、装 PyTorch、跑大模型微调,显卡的算力几乎无损地传递到 Linux 环境里。我实测过同一张 RTX 4070 Ti SUPER,在 WSL2 里跑 PyTorch 的矩阵乘法基准测试,和原生 Ubuntu 的差距在 3% 到 5% 之间,日常开发完全感知不到。而换来的是 Windows 侧的办公、娱乐、截图工具全都能用,这种便利性对独立开发者和小团队来说太重要了。
不过这套方案也不是没有门槛。显卡驱动的版本、WSL 内核的版本、CUDA 的版本、PyTorch 的版本,这四个东西之间有一条隐形的兼容链,任何一环对不上,你就会看到torch.cuda.is_available()返回False,或者更糟,直接报CUDA error: unknown error。我见过太多人在这一步卡住,然后去网上搜各种偏方,最后把环境搞得一团糟。所以这篇文章我会把整条链路拆开讲清楚,从驱动安装到环境验证,每一步都告诉你为什么这么做,以及踩坑了怎么排查。
2. 环境准备:驱动、WSL 内核与发行版的选择逻辑
2.1 显卡驱动的版本陷阱
在 Windows 上装 NVIDIA 驱动,很多人习惯去官网下最新版,觉得越新越好。但在 WSL2 场景下,这个习惯可能会害了你。WSL2 的 GPU 直通依赖的是 Windows 侧的驱动,Linux 子系统里不需要单独装显卡驱动,它通过/usr/lib/wsl/lib下的库文件直接调用 Windows 驱动暴露的接口。所以 Windows 侧的驱动版本直接决定了你能用哪个版本的 CUDA。
这里有个关键概念叫CUDA Driver API 版本,你可以用nvidia-smi命令看到。这个版本号决定了你的驱动最高支持到哪个 CUDA Runtime 版本。比如驱动显示CUDA Version: 12.4,那你就不能装 CUDA 12.5 的 Toolkit,否则运行时会报版本不匹配。我建议的做法是,先确定你要用的 PyTorch 版本,然后反推它需要的 CUDA 版本,再去找支持这个 CUDA 版本的驱动。
截至我写这篇文章的时候,PyTorch 2.4 稳定版对应的是 CUDA 12.1 和 12.4 两个构建。如果你用的是 RTX 40 系显卡,建议直接上 CUDA 12.4 的构建,驱动版本选 550 以上。驱动下载的时候注意选Game Ready Driver还是Studio Driver,两者在 CUDA 支持上没有区别,Studio 驱动更新频率低一些,稳定性更好,做开发我一般推荐 Studio。
注意:如果你之前装过旧版驱动,建议用 DDU(Display Driver Uninstaller)在安全模式下彻底卸载再装新版,避免残留的驱动文件导致 WSL2 里识别不到 GPU。
2.2 WSL2 的启用与内核更新
Windows 11 默认已经集成了 WSL2 的支持,但你需要手动启用几个 Windows 功能。最省事的方式是用管理员权限打开 PowerShell,执行wsl --install,这条命令会自动启用虚拟机平台、安装 WSL2 内核、并下载一个默认的 Ubuntu 发行版。但我不建议完全依赖这条命令,因为它装的 Ubuntu 版本可能不是你想要的,而且默认装在 C 盘。
我习惯分步操作。先启用功能:
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart然后重启电脑,再去微软官网下载最新的 WSL2 内核更新包安装。这一步很多人会跳过,但内核版本太旧会导致 GPU 直通功能不可用。装完之后用wsl --version确认内核版本,建议在 5.15 以上。
接下来是安装发行版。微软商店里搜 Ubuntu 就能看到 22.04 LTS 和 24.04 LTS 两个版本。我推荐 22.04 LTS,因为它的软件源和 CUDA 支持最成熟,24.04 虽然更新,但有些深度学习库的依赖还没完全跟上。安装完成后第一次启动会让你设置用户名和密码,这个密码是 sudo 用的,记牢。
2.3 把发行版迁移到非系统盘
默认情况下 WSL2 的虚拟磁盘文件(ext4.vhdx)放在 C 盘的用户目录下,这个文件会随着你装环境越来越大,几十个 G 是常态。C 盘空间紧张的话,建议迁移到 D 盘或专门的 SSD 上。操作方法是先导出再导入:
wsl --shutdown wsl --export Ubuntu-22.04 D:\wsl\ubuntu22.04.tar wsl --unregister Ubuntu-22.04 wsl --import Ubuntu-22.04 D:\wsl\ubuntu22.04 D:\wsl\ubuntu22.04.tar --version 2导入之后默认登录用户会变成 root,需要修改/etc/wsl.conf加一行[user]段指定默认用户。这个细节网上很多教程没提,导致新手导入后发现自己变成了 root,之前配的环境变量全没了。
3. CUDA Toolkit 在 WSL2 里的安装与验证
3.1 为什么不能在 WSL2 里装显卡驱动
这是新手最容易犯的错误。你在 WSL2 的 Ubuntu 里执行apt install nvidia-driver-550,装完之后nvidia-smi可能直接报错,或者 GPU 信息显示不全。原因在于 WSL2 的 GPU 是半虚拟化的,Linux 侧看到的是一个特殊的设备节点,真正的驱动运行在 Windows 宿主上。你只需要在 Linux 里装 CUDA Toolkit,不需要也不应该装显卡驱动。
正确的做法是,在 WSL2 里只安装 CUDA Toolkit 的 runtime 和开发工具。NVIDIA 官方提供了 WSL-Ubuntu 专用的源,配置方法如下:
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-4注意包名是cuda-toolkit-12-4而不是cuda,后者会尝试安装驱动,在 WSL2 里会出问题。装完之后需要配置环境变量,在~/.bashrc里加上:
export PATH=/usr/local/cuda-12.4/bin:$PATH export LD_LIBRARY_PATH=/usr/local/cuda-12.4/lib64:$LD_LIBRARY_PATH然后source ~/.bashrc,执行nvcc -V应该能看到 CUDA 编译器的版本信息。如果这一步报command not found,检查一下路径里的版本号是否和你装的一致。
3.2 nvidia-smi 在 WSL2 里的特殊表现
在 WSL2 里执行nvidia-smi,你会看到和 Windows 上几乎一样的输出,包括显卡型号、驱动版本、CUDA Version、显存占用等。但有一个细节要注意:WSL2 里的nvidia-smi显示的进程列表通常只有它自己,看不到 Windows 侧运行的图形程序占用的显存。这是因为两个系统的进程空间是隔离的,显存是共享的但进程信息不互通。
如果你发现nvidia-smi命令找不到,先确认/usr/lib/wsl/lib这个目录是否存在,里面应该有libcuda.so等文件。这个目录是 WSL2 自动挂载的,如果不存在,说明你的 WSL 内核版本太旧或者 GPU 直通没启用。可以尝试wsl --update更新内核,然后在 Windows 侧确认显卡驱动正常。
还有一个常见问题是显存显示为 0 或者 N/A。这通常是因为 Windows 侧的驱动版本和 WSL 内核不匹配。我遇到过一次,驱动是 535 版本,WSL 内核是 5.10,结果nvidia-smi能跑但显存信息读不出来。升级驱动到 550 以上、内核更新到 5.15 之后就正常了。
3.3 验证 CUDA 是否真正可用
装完 Toolkit 之后,别急着装 PyTorch,先用 CUDA 自带的样例程序验证一下。NVIDIA 提供了一个deviceQuery工具,在/usr/local/cuda-12.4/extras/demo_suite/目录下。编译运行:
cd /usr/local/cuda-12.4/extras/demo_suite/ sudo make deviceQuery ./deviceQuery如果输出里能看到你的显卡型号、计算能力(Compute Capability)、显存大小,并且最后显示Result = PASS,说明 CUDA 环境是通的。这一步能排除掉大部分底层问题,比直接跑 PyTorch 报错再去猜要高效得多。
我特别建议做这个验证,因为 PyTorch 的报错信息有时候会误导人。比如CUDA error: no kernel image is available for execution on the device,这个错误可能是 PyTorch 版本和显卡计算能力不匹配,也可能是 CUDA 环境本身有问题。先用 deviceQuery 确认底层没问题,再去排查上层框架,思路会清晰很多。
4. PyTorch 环境搭建:conda 还是 venv,以及版本匹配的硬逻辑
4.1 包管理器的选择与 conda 命令找不到的解决
在 WSL2 里搭 Python 环境,我推荐用 Miniconda 而不是系统自带的 Python。原因很简单,深度学习项目经常需要切换不同版本的 PyTorch 和 CUDA,conda 的环境隔离做得比 venv 更彻底,尤其是涉及到 CUDA 库的依赖管理时。系统 Python 装包容易污染全局环境,而且 Ubuntu 22.04 自带的 Python 3.10 有时候和最新的 PyTorch 有兼容性问题。
安装 Miniconda 的步骤很标准:
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh bash Miniconda3-latest-Linux-x86_64.sh安装过程中会问你是否初始化 conda,选 yes。装完之后重新打开终端,应该能看到命令行前面有(base)标识。如果没看到,执行source ~/miniconda3/bin/activate手动激活。
这里有个高频问题:很多人在 PowerShell 里执行conda activate pytorch会报无法将"conda"项识别。这是因为 conda 是装在 WSL2 的 Linux 环境里的,PowerShell 是 Windows 的终端,两者不互通。你要么在 WSL2 的终端里操作,要么在 PowerShell 里用wsl conda activate pytorch的方式调用。我建议直接打开 Windows Terminal,新建一个 Ubuntu 标签页,所有操作都在里面做,省得混淆。
4.2 PyTorch 安装命令的版本拆解
PyTorch 官网的安装命令生成器很好用,但很多人直接复制粘贴,不理解里面的参数含义。我拿一个典型的命令拆开讲:
conda install pytorch torchvision torchaudio pytorch-cuda=12.4 -c pytorch -c nvidiapytorch-cuda=12.4这个参数是关键,它告诉 conda 安装链接了 CUDA 12.4 的 PyTorch 构建。如果你写成cudatoolkit=12.4,那是旧版的写法,在新版 PyTorch 里已经废弃了。-c pytorch指定从 PyTorch 官方频道下载,-c nvidia是从 NVIDIA 频道下载 CUDA 相关的依赖库。
装完之后一定要验证:
import torch print(torch.__version__) print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0)) print(torch.cuda.get_device_capability(0))四个输出分别告诉你 PyTorch 版本、CUDA 是否可用、显卡型号、计算能力。如果is_available()返回False,先别急着重装,按下面的顺序排查:第一,确认nvidia-smi能正常输出;第二,确认 PyTorch 版本和 CUDA 版本匹配;第三,确认没有装成 CPU 版本的 PyTorch。CPU 版本的包名里通常带cpu后缀,装错了就只能重装。
4.3 计算能力与 PyTorch 构建的对应关系
显卡的计算能力(Compute Capability)决定了它能运行哪些 CUDA 内核。比如 RTX 40 系是 8.9,RTX 30 系是 8.6,RTX 20 系是 7.5。PyTorch 的预编译包通常会包含多个计算能力的支持,但如果你用的是比较新的显卡,而 PyTorch 版本比较旧,就可能出现no kernel image的错误。
我整理了一个简单的对照表,方便你快速判断:
| 显卡系列 | 计算能力 | 推荐 PyTorch 版本 | 推荐 CUDA 版本 |
|---|---|---|---|
| RTX 40 系 | 8.9 | 2.2 及以上 | 12.1 / 12.4 |
| RTX 30 系 | 8.6 | 1.12 及以上 | 11.8 / 12.1 |
| RTX 20 系 | 7.5 | 1.10 及以上 | 11.7 / 11.8 |
| GTX 16 系 | 7.5 | 1.10 及以上 | 11.7 / 11.8 |
如果你不确定自己的显卡计算能力,用torch.cuda.get_device_capability(0)就能看到。这个信息在编译自定义 CUDA 算子的时候特别重要,因为你需要设置TORCH_CUDA_ARCH_LIST环境变量来指定目标架构。
5. 实测性能与常见故障的排查链路
5.1 WSL2 与原生 Linux 的性能差距实测
我用同一张 RTX 4070 Ti SUPER 做了几组对比测试,分别在 WSL2 和原生 Ubuntu 22.04 上运行。测试项目包括 ResNet-50 训练、BERT 微调、以及纯矩阵乘法基准。结果如下:
| 测试项目 | WSL2 耗时 | 原生 Ubuntu 耗时 | 性能差距 |
|---|---|---|---|
| ResNet-50 单 epoch | 42.3s | 40.8s | 3.7% |
| BERT-base 微调 | 156s | 151s | 3.3% |
| 4096x4096 矩阵乘 | 8.2ms | 7.9ms | 3.8% |
| 数据加载(IO 密集) | 12.5s | 9.1s | 37% |
可以看到,计算密集型的任务差距很小,在 4% 以内,日常开发完全够用。但 IO 密集型的任务差距明显,尤其是涉及大量小文件读取的时候。这是因为 WSL2 访问 Windows 文件系统(比如/mnt/d/)要经过一层网络协议转换,性能损耗很大。
所以我的建议是,数据集和代码都放在 WSL2 的 Linux 文件系统里,也就是~/目录下,不要放在/mnt/c/或/mnt/d/。如果你必须访问 Windows 的文件,尽量打包成大文件再传输,减少小文件 IO。我一般会在 Windows 侧把数据集压缩成 tar 包,然后在 WSL2 里解压到 home 目录,这样能省不少时间。
5.2 torch.cuda.is_available() 返回 False 的完整排查
这是最高频的问题,没有之一。我按排查顺序列一下:
第一步,在 WSL2 终端执行nvidia-smi。如果命令找不到,说明 WSL 的 GPU 直通没启用,检查 Windows 驱动和 WSL 内核版本。如果命令能跑但报错,看错误信息,通常是驱动版本不匹配。
第二步,确认 PyTorch 是 GPU 版本。执行pip list | grep torch或conda list | grep torch,看包名里有没有cpu字样。如果有,卸载重装 GPU 版本。
第三步,检查 CUDA 版本匹配。nvidia-smi右上角显示的 CUDA Version 是驱动支持的最高版本,PyTorch 需要的 CUDA 版本不能高于这个。比如驱动显示 12.4,你装 PyTorch 的 cu121 构建是可以的,cu124 也行,但 cu125 就不行。
第四步,检查环境变量。echo $LD_LIBRARY_PATH看看有没有包含 CUDA 的 lib64 目录。有时候 conda 环境会覆盖这个变量,导致找不到 CUDA 库。
第五步,如果以上都正常,尝试重装 PyTorch。有时候是安装过程中网络问题导致包不完整,重装能解决。
我遇到过一次比较诡异的情况,nvidia-smi正常,PyTorch 也是 GPU 版本,但is_available()就是 False。最后发现是 WSL2 的~/.bashrc里有一行旧的export CUDA_VISIBLE_DEVICES="",把显卡屏蔽了。这个变量是之前跑多卡测试时加的,忘了删。所以排查的时候也要检查一下有没有这类环境变量在捣乱。
5.3 显存不足与 OOM 的应对策略
WSL2 的显存是和 Windows 共享的,如果你在 Windows 侧开了浏览器、游戏或者其他占用显存的程序,WSL2 里能用的显存就会减少。我遇到过在 WSL2 里训练模型时 OOM,但nvidia-smi显示显存占用并不高,最后发现是 Windows 侧的 Chrome 开了硬件加速,占了好几个 G 的显存。
解决办法有两个:一是训练前关掉不必要的图形程序;二是在 WSL2 的配置文件.wslconfig里限制内存使用,间接影响显存分配。.wslconfig放在 Windows 用户目录下,内容如下:
[wsl2] memory=32GB processors=16 swap=8GB这个配置限制 WSL2 最多用 32G 内存和 16 个核心。显存没有直接的配置项,但减少内存压力能间接缓解显存碎片问题。另外,PyTorch 的torch.cuda.empty_cache()可以在不用的時候释放缓存,但别频繁调用,会影响性能。
如果模型确实太大,单卡放不下,可以考虑梯度累积、混合精度训练、或者模型并行。这些属于训练技巧,不在本文范围内,但思路是有的。
6. 日常开发中的效率技巧与配置固化
6.1 用 VS Code Remote 连接 WSL2
在 WSL2 里写代码,最舒服的方式是用 VS Code 的 Remote 功能。装一个WSL扩展,然后在 WSL2 终端里执行code .,VS Code 会自动连接到 WSL2 环境,终端、调试、Python 解释器全都跑在 Linux 侧。这样你既能用 Windows 的图形界面,又能享受 Linux 的开发体验。
配置 Python 解释器的时候,选 conda 环境里的那个,路径通常是~/miniconda3/envs/pytorch/bin/python。VS Code 会自动识别 conda 环境,在左下角显示当前环境名。调试的时候断点、变量查看都正常,和本地开发没区别。
有一个小坑要注意:VS Code 的 Remote 连接有时候会因为 WSL2 的网络问题断开,尤其是电脑休眠唤醒之后。如果遇到连接不上,在 WSL2 终端执行sudo service ssh restart或者直接重启 WSL(wsl --shutdown然后重新打开)通常能解决。
6.2 环境配置的备份与迁移
配好一套能跑的环境不容易,建议定期备份 conda 环境。最直接的方式是导出环境文件:
conda env export > environment.yml这个文件记录了所有包的精确版本,换机器的时候用conda env create -f environment.yml就能复现。但要注意,CUDA 相关的包有时候在不同机器上会有差异,因为 conda 会根据系统环境选择不同的构建。如果迁移后 GPU 不可用,检查一下pytorch-cuda的版本是否匹配新机器的驱动。
另一个备份方式是直接打包整个 conda 环境目录,但这样体积很大,而且路径变了之后可能出问题。我一般只用environment.yml,配合一个记录 CUDA 版本和驱动版本的 README,足够用了。
6.3 常见误区和经验总结
最后分享几个我踩过的坑和总结的经验。第一,不要在 WSL2 里装 NVIDIA 驱动,这是最常见的错误,会导致各种奇怪的问题。第二,数据集放 Linux 文件系统,别放/mnt/下,IO 性能差距很大。第三,PyTorch 版本、CUDA 版本、驱动版本三者要匹配,装之前先查清楚。第四,.wslconfig限制内存能避免 WSL2 吃光宿主机内存导致卡顿。第五,定期wsl --update更新内核,新内核通常修复了 GPU 直通的 bug。
还有一个细节,WSL2 的时钟有时候会和 Windows 不同步,导致torch.cuda的某些操作报超时错误。如果遇到莫名其妙的时间相关报错,在 WSL2 里执行sudo hwclock -s同步一下时钟。这个问题不常见,但遇到了很难排查,记一下备用。
这套环境我用了大半年,跑过 PyTorch 训练、ONNX 导出、llama.cpp 推理,整体稳定性很好。唯一需要注意的是 Windows 更新有时候会重置 WSL 的网络配置,导致 GPU 直通失效,重新wsl --shutdown再启动就能恢复。如果你也在这条路上折腾,希望这些经验能帮你少走点弯路。