☰
Win11+WSL2+Docker深度学习环境部署与GPU直通实践
2026/10/28 20:11:29 网站建设 项目流程

1. 环境方案的整体设计与取舍

把 WIN11+WSL2+Docker 深度学习环境部署 这件事拆开看,其实只有三个动作:让 Windows 把显卡和内存借出去、让 WSL2 成为一个合格的 Linux 宿主、让 Docker 在这个宿主里把框架和依赖封起来。听起来简单,但我见过太多人卡在第二步和第三步之间反复重装系统。这篇文章面向的是手上有台带独显的 Win11 笔记本或台式机、想跑 PyTorch / TensorFlow 训练和推理、又不想折腾双系统的人。零基础能照着做,有经验的可以直接跳到参数和踩坑部分。

1.1 为什么是 WIN11 + WSL2 + Docker 这条链路

先把这条链路的角色分工说清楚,不然后面每一步都会觉得是黑魔法。Windows 是硬件所有者,负责显卡驱动、内存调度、磁盘和固件虚拟化;WSL2 是一个跑在轻量级虚拟机里的真 Linux 内核,负责提供 Linux 用户态、包管理和文件系统;Docker 在这个 Linux 内核上做进程级隔离,负责把 CUDA 运行时、Python、框架版本锁死在一个镜像里。三层各管一段,互不越界,这是它能长期稳定的根本原因。

对比一下纯 Windows 原生跑 PyTorch。原生 Windows 版 PyTorch 这些年确实能用了,但生态上的滞后是实打实的:某些算子没有 Windows 实现、编译自定义 CUDA 扩展需要 MSVC 工具链、很多开源仓库的安装脚本默认就是 bash。而 WSL2 里你拿到的是一套标准 Ubuntu,别人在服务器上怎么跑,你在这儿就怎么跑,复现成本几乎为零。

再对比双系统。双系统的问题不是装不上,是切换成本太高。你写代码、查文档、开会议软件都在 Windows 上,为了跑一次训练重启一次机器,一天重启五次,人先崩了。WSL2 的窗口是并存的,VS Code 连进去、浏览器开在 Windows、训练跑在 Linux,这才是能长期用下去的状态。

Docker 这一层则是为了解决"上次还能跑,这次就报错"这个经典问题。深度学习环境最怕的不是装不上,是装上了但版本组合悄悄变了。CUDA 小版本、cuDNN、框架 wheel、Python 版本,四者任意一个错位,症状可能是编译报错,也可能是精度异常这种更难查的东西。用镜像把这些锁死后,环境变成了一个可复制的文件,而不是一台机器的当前状态。

1.2 三种主流方案的横向对比

我把实际用过的三种组合列出来,你可以直接对号入座。这里说的"成本"指的是长期维护成本,不是一次性安装时间。

方案上手速度GPU 性能损耗环境隔离适合场景主要痛点
Windows 原生 + conda快几乎无损弱,靠 conda 环境只做推理、轻量微调算子缺失、扩展编译难、脚本不兼容
WSL2 + conda(无 Docker)较快约 3%~8%中,靠虚拟环境个人开发、单项目为主环境漂移、依赖冲突、迁移麻烦
WSL2 + Docker中等约 5%~10%强,镜像级隔离多项目并行、需要复现磁盘占用大、挂载与权限细节多
双系统 / 独立 Linux 机慢无损强长期大模型训练切换成本高、硬件投入

表里的性能损耗是实测的大致区间,来源主要是文件 I/O 跨层和容器网络开销,纯计算部分的差距远小于这个数。如果你的瓶颈在数据加载,那 WSL2 的 I/O 损耗会比表里更明显,这时候更该关注的是把数据集放在 Linux 侧而不是挂载 Windows 盘。

我的结论是:只要你打算长期在这台机器上做深度学习,就选 WSL2 + Docker。一次性多花两小时,后面省下的是几十次"环境重装"。

1.3 硬件与系统前置条件清单

装之前先核对,别急着敲命令。下面的表是我建议的底线配置,低于这个数不是不能跑,是跑起来会很别扭。

项目最低要求建议配置说明
系统版本Win11 21H2Win11 22H2 及以上新版本 WSL 支持 mirrored 网络、autoMemoryReclaim 等特性
CPU支持 VT-x / AMD-V8 核以上核数直接决定processors参数上限
内存16GB32GB 及以上WSL 上限一般取物理内存的 50%~60%
系统盘剩余 60GB剩余 150GB 以上镜像、容器层、数据集是吃盘大户
显卡支持 CUDA 的独显显存 8GB 以上显存决定你能跑多大的 batch
固件开启虚拟化同时开启 IOMMU(可选)关着虚拟化会直接报 0x80370102

磁盘这一项特别要强调。很多人装完发现 C 盘爆了,原因是 WSL 的虚拟磁盘和 Docker 的镜像层默认都落在系统盘,而且这两个虚拟磁盘只增不减——你删了 30G 文件,vhdx 文件大小不会自动缩回去。所以要么一开始就把它们挪到数据盘,要么后面学会手动压缩,这个在第 6 章会详细讲。

注意:如果你的机器是公司配发的,固件里的虚拟化和 Hyper-V 相关选项可能被策略锁定。这种情况先确认能否自行调整,不要盲目改注册表绕过。

2. 系统底座搭建:虚拟化、WSL2 与内核

这一章是整条链路的地基,地基歪了后面全是玄学问题。我把它拆成四步:开固件开关、装 WSL2、调参数、配 systemd。每一步都有明确的验证方式,做完一步验一步,不要一口气全敲完再排查。

2.1 固件虚拟化开关的具体位置

重启进 BIOS/UEFI,找带Virtualization字样的选项。Intel 平台一般叫Intel Virtualization Technology或VT-x,AMD 平台叫SVM Mode。某些主板还会有一个VT-d或IOMMU,这个对纯 WSL2 不是必须的,但如果你以后想玩设备直通,开着更省事。

进系统后验证:打开任务管理器,切到"性能"页,点 CPU,右下角会显示"虚拟化:已启用"。如果这里显示已禁用,别往下走,先去固件里改。还有个更细的检查方式是在 PowerShell 里跑systeminfo,最后几行会列出 Hyper-V 相关要求,全部显示"是"才算干净。

Windows 功能里需要开启的是"虚拟机平台"和"适用于 Linux 的 Windows 子系统"这两项。用wsl --install安装时它会自动帮你打开,但如果是手动装或者装过旧版本,建议自己确认一遍。开启后必须重启,不重启的话后续命令会报一些看起来毫不相关的错误。

实操心得:如果你之前装过旧版 WSL 或者装过第三方虚拟机软件,先把它们彻底卸载干净再装。残留的虚拟网卡和 Hyper-V 虚拟交换机是端口转发失败、网络不通的头号嫌疑。

2.2 一条命令装完 WSL2 与发行版

以管理员身份打开 PowerShell,一条命令搞定:

wsl --install -d Ubuntu-22.04

这条命令会做四件事:启用所需 Windows 功能、下载并安装最新 WSL 内核、把 WSL2 设为默认版本、拉取指定的发行版。装完会要求重启,重启后首次进入会让你设置 UNIX 用户名和密码,这个账号就是你的日常账号,不要用 root 直接干活,需要提权时用 sudo。

Ubuntu 版本怎么选?22.04 LTS 是目前兼容性最稳的选择,主流框架的官方 wheel 和 CUDA 镜像都对它做过充分验证。24.04 更新但部分第三方包还没跟上,如果你是新手,选 22.04 少踩坑。老项目可能需要 20.04,也可以共存,两个发行版互不干扰。

常用管理命令我列在这里,建议存到备忘录:

wsl -l -v # 列出所有发行版及版本 wsl --set-version Ubuntu-22.04 2 # 强制指定为 WSL2 wsl --update # 更新 WSL 内核 wsl --shutdown # 彻底关闭(改配置后必用) wsl --set-default-version 2

wsl -l -v里的 STATE 字段如果长期显示 Running 但你并没有开终端,说明后台还有进程在跑,改完.wslconfig之后必须wsl --shutdown才会生效,这一步漏掉的话你会以为参数没起作用。

2.3 .wslconfig 参数逐项拆解与取值计算

这个文件放在C:\Users\<你的用户名>\.wslconfig,注意是Windows 用户目录,不是 Linux 里的/etc/wsl.conf。两个文件长得很像但作用域完全不同,混淆是新手最常见的错误之一。

[wsl2] memory=20GB processors=10 swap=16GB swapFile=D:\\wsl\\swap.vhdx localhostForwarding=true autoMemoryReclaim=gradual sparseVhd=true

memory是 WSL2 虚拟机可用的最大内存。取值逻辑:物理内存减去 Windows 常驻占用,再留 2~4GB 余量。32GB 的机器取 20GB 比较稳,16GB 的机器取 8~10GB。不要把它设成全部物理内存,Windows 本身被挤到换页会卡到无法使用。

processors是虚拟 CPU 数量,一般取物理核心数的 60%~75%。注意是核心数不是线程数,8 核 16 线程的机器填 10 左右合适。填满全部逻辑核并不会更快,反而会因为调度竞争让训练和桌面一起变卡。

swap是交换分区大小,训练时偶尔会有内存尖峰,留 8~16GB 能救命。但如果你的程序频繁用 swap,说明 batch size 或数据加载配置有问题,swap 只是缓冲不是解决方案。swapFile指定到数据盘可以减轻系统盘压力。

autoMemoryReclaim=gradual是较新版本 WSL 才支持的参数,让空闲内存逐步归还给 Windows。这个特性解决的是"WSL 用完 20GB 后一直不释放,Windows 内存告急"的老问题,建议开。

sparseVhd=true让虚拟磁盘支持稀疏分配,删文件后有机会回收空间。但注意它只是有机会,不等于自动压缩,真正回收还是要手动操作。

改完执行wsl --shutdown,等 8 秒左右再重新进入,用free -h和nproc验证。看到数值对了,说明配置生效。

2.4 systemd、默认用户与 /etc/wsl.conf

较新的 WSL 支持 systemd,开了以后很多服务能像正常 Linux 一样管理。配置文件是/etc/wsl.conf:

[boot] systemd=true [user] default=yourusername [interop] appendWindowsPath=false

systemd=true打开后,systemctl就能用了,这对装 Docker Engine(而不是用 Docker Desktop)很关键。开启后systemd会拉起一堆服务,启动稍慢但值得。

appendWindowsPath=false是我强烈建议的一项。默认情况下 WSL 会把 Windows 的 PATH 拼进 Linux 的 PATH,导致echo $PATH长得离谱,每次 bash 启动都要遍历一堆 Windows 目录,终端明显变慢,而且容易出现"命令找到了但是个 .exe"的诡异情况。关掉之后,需要调用 Windows 程序时用完整路径即可。

default=指定默认登录用户,避免每次进去都是 root。改完这些同样要wsl --shutdown重启生效。

注意:/etc/wsl.conf写错语法可能导致发行版无法启动。改之前先备份一份,出问题可以用wsl --terminate加上从 Windows 侧访问\\wsl$\路径把文件改回来。

3. GPU 直通:Windows 侧驱动与 WSL 内的 CUDA 分工

这是整篇文章最容易出错的一节,也是网上错误教程最多的地方。核心只有一句话:显卡驱动只装在 Windows 上,WSL 里绝对不能装 Linux 显卡驱动。违反这条,轻则nvidia-smi报错,重则驱动栈冲突。

3.1 驱动只装在 Windows,这条规矩不能破

原理上,WSL2 的 GPU 是通过微软的 GPU 半虚拟化接口把宿主显卡暴露给 Linux 的。Windows 侧驱动里包含了一个特殊的用户态组件,负责把 CUDA 调用转发下去。所以你在 WSL 里看到的nvidia-smi显示的驱动版本,其实就是 Windows 那个驱动的版本。

因此流程是:先在 Windows 上装 NVIDIA 官方驱动(Game Ready 或 Studio 都行,Studio 更稳),版本要足够新,太旧的驱动不带 WSL 支持。装完在 Windows 的 PowerShell 里跑一次nvidia-smi确认驱动正常,再进 WSL 跑一次,应该输出几乎一样的信息。如果 Windows 有输出、WSL 没有,问题几乎一定在 Windows 驱动版本,而不是 Linux 侧。

有一类教程会让你在 WSL 里执行.run安装包,或者apt install nvidia-driver-xxx。不要做。这会把 Linux 内核模块塞进来,和半虚拟化接口打架,最后两边都不工作。如果已经装了,卸载干净:sudo apt purge nvidia*,然后重启 WSL。

CUDA Toolkit 是另一回事。如果你只是跑 PyTorch/TensorFlow 的官方 wheel,根本不需要装 CUDA Toolkit,因为 wheel 里已经打包了运行时库。只有当你要编译自定义 CUDA 扩展、或者需要nvcc时,才需要在 WSL 里装 toolkit。装的时候选 runfile 或 apt 都行,但注意安装时取消勾选驱动那一项。

3.2 CUDA Toolkit 与 cuDNN 的安装路径

需要编译扩展的场景,我一般是这么装的。先看 Windows 驱动支持的 CUDA 版本上限,nvidia-smi右上角会显示CUDA Version: 12.x,这个数字是驱动能支持的最高版本,你装的 toolkit 不能超过它。

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

注意源地址里是wsl-ubuntu,这个仓库是专门给 WSL 准备的,和普通 Ubuntu 仓库不同。装完把路径写进环境变量:

echo 'export PATH=/usr/local/cuda-12.1/bin:$PATH' >> ~/.bashrc echo 'export LD_LIBRARY_PATH=/usr/local/cuda-12.1/lib64:$LD_LIBRARY_PATH' >> ~/.bashrc source ~/.bashrc nvcc -V

cuDNN 现在可以直接用 apt 装,省掉了手动拷贝.so的老流程:

sudo apt-get -y install libcudnn8 libcudnn8-dev

这里有个容易被忽略的细节:能跑 PyTorch 不代表 nvcc 版本对。两者是独立的,torch.version.cuda显示的是 wheel 内置的运行时版本,nvcc -V显示的是你系统装的编译版本。混用不同版本编译扩展会报一堆找不到符号的错误。

3.3 验证链路的三道关

装完必须验证,而且要分层验证,这样出问题才能定位。

第一关,Windows 侧:nvidia-smi能看到卡和驱动版本。

第二关,WSL 侧:nvidia-smi同样能看到。如果提示command not found,去检查/usr/lib/wsl/lib/下有没有nvidia-smi,这个目录是 WSL 注入的,正常应该在 PATH 里。没有的话说明 Windows 驱动太旧或没装对。

第三关,框架层:进 Python 跑

import torch print(torch.__version__) print(torch.cuda.is_available()) print(torch.cuda.device_count()) print(torch.cuda.get_device_name(0))

四行全对才算通。如果is_available()是 False,按顺序排查:装的是不是 CPU 版 wheel(看版本号后面有没有+cu121之类的后缀)、驱动版本是否低于框架要求、是不是在容器里但没传--gpus。

再补一个更硬的验证:真跑一次矩阵乘法并计时,比只看is_available()可靠得多。

import torch, time a = torch.randn(8192, 8192, device='cuda') torch.cuda.synchronize() t = time.time() for _ in range(10): b = a @ a torch.cuda.synchronize() print(f"{(time.time()-t)/10*1000:.2f} ms/iter")

如果这个数字明显偏高,通常是显卡在节能模式或者被别的程序占着显存。跑之前先确认没有其他进程在占卡。

4. Docker 与 WSL2 的集成方式

到这一步,Linux 环境和 GPU 都通了,接下来是把 Docker 接上。这里有两条路:用 Docker Desktop 走 WSL2 后端,或者直接在发行版里装 Docker Engine。前者省事,后者干净,我两个都用过。

4.1 Docker Desktop 走 WSL2 后端

Docker Desktop 装好后,第一件事是进设置把后端确认为 WSL2,然后在 Resources → WSL Integration 里勾选你的发行版。勾选之后,你在 Ubuntu 终端里直接敲docker ps就能用,不需要 sudo,因为它把 socket 和 CLI 都桥接进来了。

这个方案的好处是把镜像、容器、卷的管理交给了图形界面,磁盘位置可以在 Settings → Resources → Advanced 里直接改,不用手动搬 vhdx。对新手来说这个便利性值不少钱。

坏处是它常驻后台,占内存。我实测过,闲置状态下 Docker Desktop 相关进程会吃掉 1~2GB 内存,启动时更多。在 16GB 的机器上这个开销要算进去。另外它偶尔会出现"卡在 starting"的状态,尤其是上次非正常关机之后。

实操心得:Docker Desktop 卡启动时,先wsl --shutdown,等十秒,再从托盘退出 Docker Desktop 重新打开。九成情况能解决。还不行就检查docker-desktop这个发行版是否正常,wsl -l -v看它的状态。

镜像存储位置建议装完立刻改到数据盘,位置在 Settings → Resources → Advanced → Disk image location。改的时候会做一次数据迁移,越早改代价越小。

4.2 直接在发行版内装 Docker Engine(备选)

如果你不想多装一个后台程序,可以在 Ubuntu 里装原生 Docker。前提是第 2.4 节已经把 systemd 打开了。

sudo apt-get update sudo apt-get install -y ca-certificates curl gnupg sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \ sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt-get update sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

装完把当前用户加进 docker 组,避免每次 sudo:

sudo usermod -aG docker $USER

这条命令需要重新登录才生效,很多人执行完立刻敲docker ps发现还是权限拒绝,就是因为没重新进终端。wsl --shutdown再进一次也行。

这个方案的内存占用明显更低,但代价是你要自己管磁盘位置、清理策略和镜像加速配置。适合已经熟悉 Docker 的人。

4.3 让容器吃到 GPU:nvidia-container-toolkit

容器默认看不到显卡,必须装 toolkit 把驱动挂进去。原生 Docker 场景下:

curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | \ sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \ sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \ sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list sudo apt-get update sudo apt-get install -y nvidia-container-toolkit sudo nvidia-ctk runtime configure --runtime=docker sudo systemctl restart docker

Docker Desktop 场景更简单,它内置了支持,一般不用额外装 toolkit,直接跑就行。验证命令:

docker run --rm --gpus all nvidia/cuda:12.1.1-base-ubuntu22.04 nvidia-smi

能输出显卡信息就说明链路全通。如果报could not select device driver,说明 toolkit 没配好或者没重启 docker 服务。

--gpus all也可以用--gpus '"device=0"'指定单卡,多卡机器上这个很有用,能避免多个容器抢同一张卡。

4.4 数据卷、挂载路径与性能

容器里跑训练,代码和数据从哪来?两种方式:挂载宿主目录,或者在构建时 COPY 进去。开发阶段用挂载,方便改代码;正式跑用 COPY,保证可复现。

关键点在挂载路径选哪。Linux 侧的家目录(比如~/projects)在 ext4 文件系统上,容器挂载进去性能正常。Windows 侧的盘(/mnt/d/...)走的是 9p 协议转发,小文件随机读写性能差一个数量级。我实测过在一个含几千个小文件的图像数据集上,从/mnt/d读的耗时是从~/data读的三倍以上。

所以数据集和代码一律放 Linux 侧。如果数据在 Windows 盘上,先复制进来:

mkdir -p ~/data && cp -r /mnt/d/datasets/mydata ~/data/

复制完第一件事是sync,然后才敢删原文件。

挂载示例:

docker run --gpus all -it --rm \ -v ~/projects/demo:/workspace \ -v ~/data:/data:ro \ -w /workspace \ -p 8888:8888 \ pytorch/pytorch:2.1.0-cuda12.1-cudnn8-devel

-v ~/data:/data:ro里的ro是只读,数据集挂只读能防手滑。-p 8888:8888依赖 WSL 的 localhost 转发,Windows 浏览器直接开localhost:8888就能访问 Jupyter,不用查虚拟机 IP。

注意:容器里跑任务时,默认是以 root 身份运行的,产生的文件在宿主上属主也是 root,之后你在 WSL 里编辑会没权限。解决办法是启动时加-u $(id -u):$(id -g),或者在容器里建一个和宿主同 uid 的用户。

5. 框架层:PyTorch 与 TensorFlow 的安装与验证

到这一步环境已经成型了,最后是装框架。这部分看似最简单,其实版本对齐是最容易出问题的地方,值得单独一章。

5.1 用 conda 还是 venv

在容器里,我倾向于用镜像自带的 Python + pip,不要额外装 conda。理由是镜像已经把 CUDA 运行时和 Python 版本配好了,再叠一层 conda 只会让镜像更大、启动更慢,而且 conda 的 CUDA 运行时和系统里的可能冲突。

在宿主(非容器)里,conda 的价值大一些,主要是能管理不同 Python 版本。但如果你已经用 Docker 隔离了项目,宿主的 conda 就退化成"辅助工具"了。我的做法是:宿主装个 miniconda 备用,日常项目一律走容器。

如果坚持用 conda,建议装 miniforge 而不是完整版 Anaconda,体积小一半,包管理用 conda-forge 通道,解析速度也快些。

5.2 版本对齐表与常用安装命令

版本对齐的逻辑是:框架 wheel → CUDA 运行时 → 驱动最低版本,从右往左满足。先看驱动支持的 CUDA 上限,再选不超过它的框架 wheel。

框架版本推荐 Python内置 CUDA驱动最低版本备注
PyTorch 2.13.10 / 3.1112.1525 左右兼容性最广,推荐
PyTorch 2.33.10~3.1212.1525 左右新特性多,编译扩展需注意
PyTorch 2.53.11 / 3.1212.4550 左右新卡支持好
TensorFlow 2.153.9~3.1112.2525 左右官方容器镜像最省事
TensorFlow 2.16+3.11 / 3.1212.3545 左右转向 Keras 3,注意 API 变化

安装命令以 PyTorch 为例,走官方 wheel 索引,不要用默认 PyPI 的版本(那是 CPU 版):

pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121

cu121这个后缀必须和你的驱动匹配。装完立刻验证版本号后缀:

import torch print(torch.__version__) # 应该带 +cu121 print(torch.version.cuda) # 12.1 print(torch.backends.cudnn.version())

TensorFlow 用官方容器镜像最省事,省掉了自己配 CUDA 的麻烦:

docker run --gpus all -it --rm -v ~/projects:/workspace \ tensorflow/tensorflow:2.15.0-gpu-jupyter

实操心得:pip 装大包时如果中途断了,容易出现"包装了一半"的状态,症状是import torch报奇怪的符号错误。这时候别排查,直接pip uninstall torch torchvision torchaudio -y然后重装,比重装整个环境快。

5.3 跑通一个最小训练脚本

环境验证的终点不是is_available(),是能真跑完一次前向反向。我一般用下面这个小脚本,30 秒内能跑完,能同时验证 GPU、数据加载和多进程。

import torch, torch.nn as nn from torch.utils.data import DataLoader, TensorDataset dev = 'cuda' if torch.cuda.is_available() else 'cpu' print('device:', dev, torch.cuda.get_device_name(0) if dev=='cuda' else '') x = torch.randn(4096, 128) y = (x.sum(1, keepdim=True) > 0).float() ds = TensorDataset(x, y) dl = DataLoader(ds, batch_size=256, shuffle=True, num_workers=4, pin_memory=True) model = nn.Sequential(nn.Linear(128, 256), nn.ReLU(), nn.Linear(256, 1)).to(dev) opt = torch.optim.Adam(model.parameters(), lr=1e-3) lossf = nn.BCEWithLogitsLoss() for epoch in range(3): total = 0.0 for bx, by in dl: bx, by = bx.to(dev, non_blocking=True), by.to(dev, non_blocking=True) opt.zero_grad() loss = lossf(model(bx), by) loss.backward() opt.step() total += loss.item() print(f'epoch {epoch} loss {total/len(dl):.4f}') print('peak mem: %.2f GB' % (torch.cuda.max_memory_allocated()/1024**3))

num_workers=4和pin_memory=True这两项特别值得保留,它们能顺带验证多进程数据加载是否正常。在容器里跑num_workers > 0偶尔会因为共享内存不足报错,报错信息里会出现Bus error或shared memory,解决办法是启动容器时加--shm-size=8g。

这个脚本跑通,说明整条链路——Windows 驱动、WSL2 直通、容器运行时、框架 wheel、数据加载——全部正常。后面装什么项目都可以在这个基础上叠加。

6. 常见问题与排查技巧实录

这一章是我这几年被问得最多的问题汇总。我把它们整理成"症状 → 原因 → 处理"的结构,方便你直接查表。

6.1 症状对照速查表

症状最可能原因处理方式
wsl --install报 0x80370102固件虚拟化未开进 BIOS 开 VT-x / SVM
WSL 里nvidia-smi找不到Windows 驱动旧或不含 WSL 组件更新 Windows 侧驱动,不要装 Linux 驱动
torch.cuda.is_available()为 False装成 CPU 版 wheel用官方 cu 索引重装
容器里报could not select device driver未装/未配 nvidia-container-toolkit装 toolkit 并重启 docker
Docker 拉镜像卡住或超时网络问题配置可用的 registry mirror 或改用内网仓库
训练到一半进程被杀WSL 内存上限触发 OOM调大memory或减小 batch
C 盘空间持续下降vhdx 只增不减关闭 WSL 后手动压缩虚拟磁盘
数据加载慢到离谱数据放在/mnt/c或/mnt/d迁移到 Linux 家目录
终端启动明显变慢Windows PATH 被拼接appendWindowsPath=false
Jupyter 打不开 localhost端口转发异常检查localhostForwarding,或改用实际 IP
容器内保存文件后宿主无权限容器以 root 运行启动加-u $(id -u):$(id -g)
时间显示错乱宿主与 WSL 时钟漂移wsl --shutdown后重进,或手动同步

6.2 磁盘与性能这两个坑,值得单独说

虚拟磁盘膨胀是所有 WSL2 用户迟早会遇到的问题。ext4.vhdx默认在%LOCALAPPDATA%\Packages\下面,你删掉 50GB 数据,文件大小纹丝不动。回收方式分两种。

较新版本的 WSL 直接支持压缩:

wsl --shutdown wsl --manage Ubuntu-22.04 --set-sparse true

老版本用 diskpart:

wsl --shutdown diskpart # 在 diskpart 里依次执行 # select vdisk file="C:\Users\你的用户名\AppData\Local\Packages\...\ext4.vhdx" # attach vdisk readonly # compact vdisk # detach vdisk # exit

压缩前必须wsl --shutdown,否则文件被占用会失败。压缩过程可能几分钟,视磁盘大小而定。

第二个坑是文件系统边界。我前面反复强调数据要放 Linux 侧,但很多人不理解为什么。简单类比:Windows 盘对 WSL 来说像是一个"网络共享目录",每次读写都要经过协议转换,单个大文件还好,几千个小文件时开销就爆炸了。深度学习数据集恰恰是大量小文件的典型场景。判断方法很简单,把同一个数据集放在两个位置各读一遍,用time计时,差距会非常直观。

还有一个隐蔽的性能问题:num_workers设得过高。在 WSL2 里,数据加载进程和主进程共享同一个 CPU 配额,num_workers设到 16 而processors只有 8,结果就是互相抢 CPU,反而更慢。经验值是num_workers等于processors的一半左右。

6.3 备份、迁移与快速重建

环境配好之后一定要做一次导出,这是最省时间的保险。

wsl --shutdown wsl --export Ubuntu-22.04 D:\backup\ubuntu-2024.tar

导出的 tar 包含整个文件系统,几十 GB 很正常。恢复:

wsl --import Ubuntu-22.04 D:\wsl\ubuntu D:\backup\ubuntu-2024.tar --version 2

导入后默认用户会变回 root,需要重新在/etc/wsl.conf里指定default=。

项目层面,我更推荐把环境写成 Dockerfile 而不是导出整个 WSL。因为 WSL 导出的是"整台机器",而 Dockerfile 描述的是"这个项目需要什么",后者可读、可 diff、可分享。

FROM pytorch/pytorch:2.1.0-cuda12.1-cudnn8-devel RUN pip install --no-cache-dir \ numpy pandas scikit-learn matplotlib tqdm tensorboard WORKDIR /workspace ENV PYTHONUNBUFFERED=1

配合docker-compose.yml把 GPU、端口、挂载都固定下来,换机器时docker compose up一条命令就能复原。这比任何"安装笔记"都可靠,因为笔记会过期,镜像不会。

实操心得:每次装完一套能用的环境,先跑一遍最小训练脚本,然后立刻 commit 成镜像打个 tag,比如demo-env:verified-2024xx。后面无论怎么折腾,出问题docker run回这个 tag 就行,心理安全感直线上升。这个习惯帮我省掉过至少三次重装。

7. 一些实际用下来的体会

这套组合我用了几年,最大的感受是:它的价值不在于省事,而在于把不确定性关进了笼子里。真正折磨人的从来不是安装那两小时,而是三个月后你想复现一次结果,发现环境已经和当初不一样了。镜像 + 版本锁定 + 一键导出,本质上是在给未来的自己留退路。

如果让我给刚上手的人一条建议,那就是先把最小闭环跑通,再往里加东西。先让nvidia-smi在 WSL 里出来,再让容器里的nvidia-smi出来,再让 PyTorch 认到卡,最后才装你的项目依赖。每一步验证一次,出问题时你就知道该看哪一层。反过来,一口气装十几个包然后对着满屏红字猜原因,那才是真正浪费时间的做法。

还有个小技巧分享一下:我习惯在 WSL 家目录下放一个env-check.sh,把nvidia-smi、nvcc -V、python -c "import torch..."、docker run --gpus all ...这几条串起来,每次环境有变动就跑一遍,几秒钟就能知道哪一层出了问题。脚本本身不复杂,但它把"排查"这件事从"想起来要查什么"变成了"跑一下看结果",心理负担小很多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询