1. 为什么要在 Windows 上折腾 WSL2 跑 AI 训练
1.1 一个真实场景:Windows 桌面 + Linux 内核 + NVIDIA 显卡
我日常主力机是一台 Windows 11 台式机,显卡是 RTX 4070 Ti Super,16GB 显存。平时写代码、跑实验、调模型都在同一台机器上完成。早几年我的方案是双系统:Windows 用来办公和娱乐,Ubuntu 用来跑训练。听起来很美好,实际用起来非常割裂——切一次系统要重启,数据同步靠移动硬盘,IDE 配置两边各维护一套,调试到一半想查个资料还得切回去。
后来我彻底转向 WSL2。原因很简单:它把 Linux 内核跑在轻量级虚拟机里,和 Windows 共享文件系统、共享网络、共享剪贴板,最关键的是支持 GPU 直通(GPU passthrough)。也就是说,我在 Windows 里打开 VS Code,连上 WSL2 里的 Ubuntu,用里面的 Python、PyTorch、CUDA 跑训练,显卡是物理直通的,性能损耗在可接受范围内。
这套方案解决的核心问题是:不用离开 Windows 桌面环境,就能获得一个接近原生 Linux 的 AI 开发体验。适合的人群包括:只有一台 Windows 电脑的学生、需要同时兼顾办公和训练的开发者、以及不想折腾双系统但又必须用 Linux 工具链的人。
1.2 WSL2 到底是不是"虚拟机",性能损失有多少
很多人一听 WSL2 是虚拟机就摇头,觉得性能不行。这里需要把概念理清楚。
WSL2 用的是微软自家的轻量级虚拟化技术,跑的是一个真实的 Linux 内核(微软自己编译维护的版本),不是模拟层。WSL1 才是那个把 Linux 系统调用翻译成 Windows 调用的方案,兼容性和性能都有问题。WSL2 直接跑真内核,系统调用是原生的,所以绝大多数 Linux 软件都能正常跑。
性能方面,我实测下来的结论是:
| 场景 | 性能表现 | 说明 |
|---|---|---|
| CPU 计算 | 接近原生 95% 以上 | 现代 CPU 虚拟化开销很小 |
| GPU 计算 | 接近原生 95% 以上 | 通过 GPU 直通,CUDA 调用几乎无损耗 |
| 磁盘 IO(WSL 内部) | 接近原生 | ext4 文件系统在虚拟磁盘上 |
| 磁盘 IO(跨系统访问) | 明显下降 | 访问 /mnt/c 下的文件会慢很多 |
| 内存 | 动态分配 | 默认最多用宿主一半内存,可配置 |
关键点在于:代码和数据一定要放在 WSL 自己的文件系统里(比如/home/username/project),不要放在/mnt/c/...下面。跨文件系统访问的 IO 性能差距可能有 5 到 10 倍,训练时读数据集会非常明显。这个坑我踩过,当时把数据集放在 Windows 的 D 盘,训练一个 epoch 要等半天,移到 WSL 内部后速度直接起飞。
1.3 GPU 直通的前提条件清单
在动手之前,先确认你的硬件和系统满足条件,否则后面会卡在各种奇怪的地方。
- 显卡:必须是 NVIDIA 显卡。AMD 显卡在 WSL2 下的 CUDA 支持目前还不成熟,虽然网上有一些方案,但稳定性和生态都不如 NVIDIA。Intel 核显可以用于显示输出,但不参与 CUDA 计算。
- 驱动版本:Windows 端的 NVIDIA 驱动必须是较新的版本,建议 515 以上。驱动装好后,WSL2 里不需要再单独装显卡驱动,直接用 Windows 的驱动即可。
- Windows 版本:Windows 11 或者 Windows 10 版本 21H2 及以上。建议直接上 Windows 11,WSL2 的体验更完整。
- 虚拟化:BIOS 里要开启 CPU 虚拟化(Intel VT-x 或 AMD-V),Windows 功能里要启用"虚拟机平台"和"适用于 Linux 的 Windows 子系统"。
- WSL 版本:必须是 WSL2,用
wsl --set-default-version 2设置默认版本。
注意:如果你用的是笔记本双显卡(核显 + 独显),WSL2 会正确识别 NVIDIA 独显,不用担心识别错。但要注意笔记本的电源模式,省电模式下独显可能被限制功耗,训练速度会受影响。
2. 从零开始:WSL2 与 Ubuntu 的安装配置
2.1 一条命令安装 WSL2 的现代做法
早些年装 WSL2 要手动开启一堆 Windows 功能,还要去商店下载发行版,步骤繁琐。现在微软把流程简化了,在管理员权限的 PowerShell 里执行一条命令就行:
wsl --install这条命令会自动完成:启用虚拟机平台、启用 WSL 功能、下载并安装 WSL2 内核、安装默认的 Ubuntu 发行版。执行完重启电脑,首次启动 Ubuntu 会让你设置用户名和密码。
如果你想要指定发行版,比如 Ubuntu 22.04,可以这样:
wsl --install -d Ubuntu-22.04查看可用的发行版列表:
wsl --list --online我个人的建议是选 Ubuntu 22.04 LTS 或者 24.04 LTS。22.04 的生态最成熟,各种 AI 框架的官方支持都覆盖到了;24.04 更新一些,但个别老库可能还没适配。追求稳定就 22.04。
2.2 把 WSL 装到 D 盘:避免 C 盘爆满
默认情况下,WSL 的虚拟磁盘文件(ext4.vhdx)放在 C 盘的用户目录下。这个文件会随着你装东西越来越大,跑 AI 的话动辄几十 GB,C 盘很容易被撑爆。我的做法是把 WSL 整个迁移到 D 盘。
步骤是这样的。先关闭 WSL:
wsl --shutdown然后导出当前发行版到一个 tar 文件:
wsl --export Ubuntu-22.04 D:\wsl\ubuntu22.tar注销原来的发行版:
wsl --unregister Ubuntu-22.04在 D 盘创建目标目录,然后导入:
mkdir D:\wsl\ubuntu22 wsl --import Ubuntu-22.04 D:\wsl\ubuntu22 D:\wsl\ubuntu22.tar --version 2导入完成后,默认登录用户会变成 root。要改回你自己的用户,需要修改配置文件。在 WSL 里执行:
sudo nano /etc/wsl.conf写入以下内容:
[user] default=你的用户名保存后退出,在 PowerShell 里执行wsl --shutdown重启,再进入就是你的用户了。
提示:迁移完成后,那个 tar 文件可以删掉,但建议先确认新环境一切正常再删,留个备份心里踏实。
2.3 内存与 CPU 资源的合理分配
WSL2 默认会占用宿主一半的内存,但不会主动释放。如果你宿主是 32GB 内存,WSL 可能吃掉 16GB,跑大模型时 Windows 这边就卡了。解决办法是在用户目录下创建.wslconfig文件(注意是 Windows 用户目录,比如C:\Users\你的名字\.wslconfig),内容如下:
[wsl2] memory=24GB processors=12 swap=8GB localhostForwarding=true这里我分配了 24GB 内存给 WSL(宿主 32GB),12 个逻辑核心,8GB 交换空间。具体数值根据你的硬件调整。原则是:给 WSL 留够训练用的内存,同时给 Windows 留至少 8GB 保证系统流畅。
改完这个文件后,必须执行wsl --shutdown完全关闭 WSL,再重新启动才会生效。这个点很多人会忽略,改完发现没效果,其实就是没重启。
2.4 换源与基础工具链准备
Ubuntu 默认的软件源在国内访问速度一般,换成国内镜像源能快不少。以 Ubuntu 22.04 为例,备份原配置后编辑:
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak sudo nano /etc/apt/sources.list把里面的archive.ubuntu.com和security.ubuntu.com替换成国内镜像地址。换完后更新:
sudo apt update && sudo apt upgrade -y然后装一批基础工具,这些后面都会用到:
sudo apt install -y build-essential git curl wget vim htop tmux unzipbuild-essential提供 gcc、g++、make,编译 Python 包时会用到;tmux用来在后台跑训练,断开终端也不影响;htop看资源占用;git拉代码。这些都是常规操作,但少一个后面就可能卡住。
3. GPU 直通与 CUDA 环境搭建
3.1 验证 GPU 是否被 WSL2 正确识别
装完 WSL2 后,第一件事是确认显卡能不能被看到。在 WSL 里执行:
nvidia-smi如果输出了一张表格,显示显卡型号、驱动版本、显存占用等信息,说明 GPU 直通成功了。如果提示 command not found,说明要么 Windows 端驱动没装好,要么 WSL 版本不对。
这里有个关键认知:WSL2 里不需要安装 NVIDIA 显卡驱动。驱动是 Windows 端装的,WSL2 通过一个特殊的机制(/usr/lib/wsl/lib下的库)直接调用宿主驱动。你如果在 WSL 里再装一遍驱动,反而会冲突。
如果nvidia-smi找不到,先检查这个目录:
ls /usr/lib/wsl/lib/正常情况下应该有libcuda.so、libnvidia-ml.so等文件。如果没有,说明 WSL2 的 GPU 支持没启用,可能需要更新 Windows 端的 NVIDIA 驱动到最新版。
3.2 CUDA Toolkit 的安装与版本选择
nvidia-smi能跑通只说明驱动层面通了,要编译和运行 CUDA 程序还需要 CUDA Toolkit。这里有个容易混淆的点:nvidia-smi显示的 CUDA Version 是驱动支持的最高 CUDA 版本,不是你实际安装的版本。
安装 CUDA Toolkit 有两种方式:用 NVIDIA 官方源,或者用 conda 装。我推荐用 conda 装,因为版本管理更灵活,不会污染系统环境。
先装 Miniconda:
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh bash Miniconda3-latest-Linux-x86_64.sh一路回车,最后执行source ~/.bashrc让 conda 生效。然后创建环境并安装 CUDA:
conda create -n ai python=3.10 -y conda activate ai conda install cudatoolkit=11.8 -c nvidia -y为什么选 11.8?因为它是目前 PyTorch 生态兼容性最好的版本之一,很多预编译包都基于它。如果你要用更新的特性,可以选 12.1,但要确认 PyTorch 版本对应。
注意:conda 装的
cudatoolkit只包含运行库,不包含nvcc编译器。如果你需要编译自定义 CUDA 算子,还得额外装cuda-toolkit完整包,或者用系统级安装。纯跑 PyTorch 的话,cudatoolkit就够了。
3.3 PyTorch 安装:版本匹配是核心
PyTorch 安装最容易出错的地方就是版本不匹配。CUDA 版本、PyTorch 版本、Python 版本三者必须对应。我的做法是直接去 PyTorch 官网查对应命令,不要凭记忆写。
以 CUDA 11.8 + Python 3.10 为例:
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118装完后验证:
import torch print(torch.__version__) print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0))如果cuda.is_available()返回 True,并且打印出你的显卡型号,说明环境通了。如果返回 False,八成是版本不匹配,或者装成了 CPU 版本。
我踩过的一个坑:用pip install torch不带 index-url,装出来的是 CPU 版本,因为默认源里就是 CPU 版。必须显式指定 CUDA 版本的 index-url。这个细节很多人第一次都会中招。
3.4 一个完整的验证脚本
环境搭好后,跑一个真实的 GPU 计算测试,确认不只是"能识别"而是"能计算"。下面这段代码做矩阵乘法并测速:
import torch import time device = torch.device("cuda" if torch.cuda.is_available() else "cpu") print(f"Using device: {device}") # 创建两个大矩阵 a = torch.randn(4096, 4096, device=device) b = torch.randn(4096, 4096, device=device) # 预热 for _ in range(3): c = torch.matmul(a, b) torch.cuda.synchronize() # 计时 start = time.time() for _ in range(10): c = torch.matmul(a, b) torch.cuda.synchronize() end = time.time() print(f"10 次 4096x4096 矩阵乘法耗时: {end - start:.3f} 秒") print(f"显存占用: {torch.cuda.memory_allocated() / 1024**3:.2f} GB")这段脚本能跑通并且速度正常,说明 GPU 直通和 CUDA 环境都没问题。如果速度异常慢,可能是掉到了 CPU 上算,检查device是否正确。
4. 开发工作流与效率优化
4.1 VS Code 远程连接 WSL2 的正确姿势
WSL2 最舒服的开发方式是用 VS Code 的 Remote 功能。在 Windows 的 VS Code 里装 "WSL" 扩展,然后在 WSL 终端里进入项目目录执行code .,VS Code 会自动以远程模式打开这个目录。
这种模式下,VS Code 的界面跑在 Windows 上,但文件系统、终端、Python 解释器都在 WSL 里。你可以在 WSL 里装 Python 扩展、Jupyter 扩展,调试体验和本地开发几乎一样。
关键配置:在 VS Code 设置里把默认终端设为 WSL 的 shell,把 Python 解释器指向 conda 环境里的那个。这样每次打开项目,环境都是对的,不用手动切换。
4.2 文件系统布局:哪些放 WSL,哪些放 Windows
前面提过跨文件系统访问慢,这里给一个具体的布局建议:
- 代码和数据集:放 WSL 内部,比如
/home/username/projects。这是性能最优的位置。 - 需要和 Windows 共享的文件:放
/mnt/d/share这类挂载点,但只放文档、图片这类不涉及高频 IO 的东西。 - 模型权重:如果要在 Windows 和 WSL 之间共享,可以放挂载点,但加载时会慢。更好的做法是各存一份,或者用软链接。
我自己的习惯是在 WSL 里建一个~/workspace目录,所有项目都在里面。Windows 那边通过\\wsl$\Ubuntu-22.04\home\username这个路径也能访问到,需要的时候拖文件很方便。
4.3 后台训练与断点续跑
跑深度学习训练动辄几小时,不能一直开着终端。用tmux或者nohup让训练在后台跑。
tmux 的用法:
tmux new -s train # 在 tmux 会话里启动训练 python train.py # 按 Ctrl+B 然后按 D 脱离会话下次回来:
tmux attach -t train这样即使关掉终端、关掉 VS Code,训练也不会中断。WSL2 只要不执行wsl --shutdown,后台进程就一直活着。
注意:Windows 休眠或睡眠时,WSL2 的进程会被挂起。如果要长时间训练,建议把 Windows 的电源设置改成"从不睡眠",或者至少保证训练期间不休眠。
4.4 显存监控与常见性能问题
训练时用nvidia-smi看显存和 GPU 利用率。如果 GPU 利用率长期低于 50%,说明数据加载是瓶颈,需要优化 DataLoader,比如增大num_workers、开启pin_memory。
一个常见的性能陷阱:DataLoader 的num_workers设成 0,数据加载在主进程里串行执行,GPU 大部分时间在等数据。设成 4 到 8 通常能显著提升利用率。但也不是越大越好,太多 worker 会争抢 CPU 和内存。
另一个坑是pin_memory。在 WSL2 下,开启pin_memory=True能加速 CPU 到 GPU 的数据传输,但会占用更多内存。如果内存紧张,可以关掉。
5. 常见问题排查与避坑实录
5.1 WSL2 启动失败与"尚未准备就绪"
有时候执行wsl命令会提示"WSL2 尚未准备就绪"或者启动超时。这类问题通常有几个原因:
- 虚拟化没开:进 BIOS 确认 Intel VT-x 或 AMD-V 已启用。
- Windows 功能没启用:检查"虚拟机平台"和"适用于 Linux 的 Windows 子系统"是否勾选。
- WSL 内核版本旧:执行
wsl --update更新内核。 - Hyper-V 冲突:如果装了其他虚拟化软件(如 VMware),可能和 Hyper-V 冲突,需要调整配置。
排查顺序建议从简到繁:先wsl --update,再检查 Windows 功能,最后查 BIOS。
5.2 conda 命令找不到的解决
在 PowerShell 里执行conda activate报"无法将 conda 项识别",这是因为 conda 装在 WSL 里,PowerShell 里当然找不到。要在 WSL 的 bash 里执行 conda 命令。
如果是在 WSL 的 bash 里也找不到 conda,说明~/.bashrc没加载 conda 的初始化脚本。检查~/.bashrc末尾有没有类似这样的内容:
# >>> conda initialize >>> __conda_setup="$('/home/username/miniconda3/bin/conda' 'shell.bash' 'hook' 2> /dev/null)" ... # <<< conda initialize <<<没有的话,执行~/miniconda3/bin/conda init bash然后重开终端。
5.3 CUDA 版本不匹配的典型症状
CUDA 版本问题表现多样,整理成速查表:
| 症状 | 可能原因 | 解决方法 |
|---|---|---|
cuda.is_available()返回 False | 装了 CPU 版 PyTorch | 用正确 index-url 重装 |
报错CUDA driver version is insufficient | 驱动太旧 | 更新 Windows 端 NVIDIA 驱动 |
报错no kernel image is available | CUDA 版本和显卡算力不匹配 | 换对应 CUDA 版本的 PyTorch |
| 训练中途崩溃 | 显存不足 | 减小 batch size 或梯度累积 |
| 速度异常慢 | 掉到 CPU 上算 | 检查 device 和 tensor 位置 |
5.4 显存不足的应对策略
显存不足是训练大模型最常见的拦路虎。除了换更大显存的显卡,还有这些软件层面的办法:
- 减小 batch size:最直接,但可能影响收敛。
- 梯度累积:小 batch 多次前向后累积梯度再更新,等效大 batch。
- 混合精度训练:用
torch.cuda.amp,显存占用能降一半左右。 - 梯度检查点:用时间换空间,适合超深网络。
- 模型并行:把模型拆到多张卡上,但 WSL2 下多卡配置更复杂。
我实测下来,混合精度 + 梯度累积的组合能覆盖大部分显存不足的场景,性价比最高。
5.5 跨系统文件访问的性能陷阱
再强调一次这个坑:把数据集放在/mnt/c或/mnt/d下,训练时读数据会非常慢。我做过对比测试,同样一个 ImageNet 子集,放在 WSL 内部读取速度是放在挂载点的 5 倍以上。
如果你必须用 Windows 上的数据,有两个办法:一是复制到 WSL 内部(占空间但快),二是用符号链接把挂载点链接到 WSL 内部(不占额外空间但依然慢)。追求性能就选第一种。
6. 进阶玩法与扩展方向
6.1 多环境隔离:不同项目不同 CUDA 版本
不同项目可能依赖不同的 CUDA 和 PyTorch 版本,用 conda 环境隔离是最干净的方案。每个项目一个环境:
conda create -n project_a python=3.10 -y conda create -n project_b python=3.9 -y切换环境用conda activate project_a。VS Code 里可以为每个工作区指定不同的解释器,互不干扰。
6.2 模型微调与大模型推理
WSL2 的 GPU 直通对模型微调完全够用。以 LoRA 微调为例,7B 参数的模型在 16GB 显存上,用 4bit 量化 + LoRA 可以跑起来。推理方面,llama.cpp 这类框架在 WSL2 下也能用 CUDA 加速,但要注意版本兼容性,有些版本对 CUDA 版本有特定要求。
6.3 从 PyTorch 到 ONNX 的导出
训练完的模型经常要导出成 ONNX 部署。在 WSL2 里导出和在原生 Linux 里没区别:
import torch dummy_input = torch.randn(1, 3, 224, 224).cuda() model = model.cuda().eval() torch.onnx.export(model, dummy_input, "model.onnx", opset_version=13)导出时注意 opset 版本和推理引擎的兼容性,以及动态轴的处理。
6.4 备份与环境迁移
WSL2 环境搭好后,建议定期备份。用wsl --export导出成 tar 文件,换机器时wsl --import导入即可。这样重装系统或者换电脑时,环境能快速恢复,不用从头配一遍。
我个人的习惯是每配好一个稳定的环境就导出一次,标注好日期和用途。踩过的坑告诉我,环境这种东西,能备份就备份,重配一次少说半天。
最后分享一个我常用的技巧:把常用的环境配置命令写成一个 shell 脚本,比如setup_env.sh,里面包含换源、装工具、建 conda 环境、装 PyTorch 的完整流程。新机器上跑一遍脚本,半小时就能从零到能训练。这个脚本我维护了两年,每次遇到新坑就补一条,现在基本能覆盖 90% 的场景。