说实话,在Windows上编译DeepSpeed这件事,我最早是拒绝的。一个项目文档里从第一天就写明“DeepSpeed officially supports Linux”的库,强行塞到Windows上跑,光是想想就知道编译链有多难受。但后来实在绕不过去——手头的工作站装的是Windows系统,要微调大模型,又不想为了跑个实验专门去装双系统或者再买一台Linux机器。于是在某天下午,我对着VS的编译日志硬啃了几小时,终于把DeepSpeed在Windows上编译通过,并且让GPU实打实地跑起来了。
这篇文章就是想把这条完整路线记录下来。不是什么“官方推荐”做法,而是我自己反复踩坑验证过的可行方案。内容覆盖Windows下从零准备PyTorch GPU环境,到编译DeepSpeed算子,再到用GPU实际跑通一个训练例子的全过程。适合那些跟我一样,必须在Windows上做深度学习训练、大模型微调,又不想被Linux劝退的人。
1. 为什么在Windows上编译DeepSpeed这么麻烦
1.1 DeepSpeed对Windows的“官方冷漠”
DeepSpeed是微软开源的深度学习优化库,主打大模型训练时的显存优化和加速。ZeRO系列、offload到CPU/NVMe、FusedAdam这类能力,在做大模型微调时基本是刚需。但讽刺的是,微软自家的这个库对自家Windows系统从来不上心,官方文档和CI测试基本只覆盖Linux。
问题出在DeepSpeed的核心结构上。它不是一个纯Python库,而是大量使用C++和CUDA算子,编译时依赖GCC或者Clang工具链,还依赖很多Linux生态的库,比如libaio(异步I/O库)。到了Windows上,这些依赖全都不成立。MSVC编译器不是不能用,但很多DeepSpeed的C++代码从没在MSVC上测过,一编译就爆错。
我最初直接跑pip install deepspeed,装是装上了,但一跑就报错,说找不到cpu_adam之类的一堆算子实现。后来才明白,Windows下pip安装的DeepSpeed默认跳过了所有算子编译,装出来的只是一个“空壳”,真正干活时根本用不了。
1.2 一条切实可行的路线
经过多次尝试,我最后确定了一条可行路径:在Windows上用MSVC编译DeepSpeed源码,并且按需开启必要的算子编译。
核心思路有几个关键点:
- 不要试图全量编译。DeepSpeed的算子很多,但在Windows上跑,真正常用的也就那么几个:
CPUAdam(CPU端优化器算子)、FusedAdam(融合Adam)、以及ZeRO所需的通信原语。只开需要的编译模块,省略掉libaio这类Windows上根本不存在的依赖。 - 使用源码安装而不是pip直接装。从GitHub拉下源码,设置好编译开关,用
python setup.py build_ext手动编译算子,这样才能确保需要的东西真正被编出来。 - 配合正确的PyTorch GPU版。DeepSpeed依赖PyTorch的C++扩展机制,如果PyTorch本身没装对,后续编译必然连环踩坑。
这条路走通之后,我没再遇到什么解决不了的大问题。编译一次,以后直接复用,对Windows用户来说算是能接受的方案了。
2. 环境准备:先把地基打牢
2.1 工具链清单一览
编译DeepSpeed之前,得先把整个工具链对齐。我在实际准备环境中发现,大多数人编译失败不是因为DeepSpeed本身难搞,而是前置环境版本不匹配。我把核心依赖列成了一个清单:
| 组件 | 推荐版本 | 说明 |
|---|---|---|
| Windows | Windows 10/11 64位 | 无特殊要求 |
| Python | 3.10或3.11 | 不要用3.12,部分算子适配不完善 |
| CUDA Toolkit | 12.1或11.8 | 选PyTorch官方已验证的版本 |
| cuDNN | 与CUDA版本匹配 | 训练性能必需 |
| VS Build Tools | Visual Studio 2022 | 勾选“使用C++的桌面开发” |
| Git | 任意较新版本 | 拉取DeepSpeed源码 |
| PyTorch | 2.6.0(GPU版/CUDA 12.1) | 使用--index-url指定CUDA版本来源 |
这里单独说一句Python版本的问题。我之前试着用Python 3.12装DeepSpeed,结果碰到一堆算子编译时error: no matching constructor for initialization之类的报错,查了半天,是编译器对C++标准支持和Python 3.12扩展API不兼容导致的。换回3.10之后一次通过。所以稳妥起见,3.10是最省心的选择。
2.2 安装CUDA与VS Build Tools的关键细节
CUDA Toolkit的安装看着简单,其实埋了不少雷。装完之后很多人直接跑nvcc -V发现找不到命令,原因就是环境变量没生效。安装包默认会把CUDA装到C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.1,但bin目录并没有自动加入Path。
装完之后一定要手动检查一下:
C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.1\bin手动把这条路径加进系统环境变量的Path中。注意,是加bin这一层,不是加CUDA根目录。我见过有人直接把根目录加进去,最后nvcc还是识别不了,白白浪费半天排查时间。
VS Build Tools的安装是另一个重点。打开Visual Studio Installer,勾选“使用C++的桌面开发”工作负载,里面会包含MSVC编译器、Windows SDK和CMake工具。这一步如果漏了,后面编译DeepSpeed时会直接报Microsoft Visual C++ 14.0 or greater is required,根本走不下去。
另外,我在实操中还发现一个技巧:编译时最好用“x64 Native Tools Command Prompt for VS 2022”这个终端,而不是普通的cmd或PowerShell。这个终端会自动配置好MSVC编译器路径和Windows SDK环境变量,省掉很多手工设置环境的功夫。
2.3 创建一个干净的conda环境
环境这块我强烈建议用conda或者venv隔离,不要直接装在系统Python里。我在一个满是旧依赖的系统Python上编译DeepSpeed时,被各种版本的numpy、setuptools冲突折腾得够呛。
创建环境的命令很简单:
conda create -n deepspeed python=3.10 -y conda activate deepspeed如果网络速度不理想,建议先给conda配置清华源:
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ conda config --set show_channel_urls yes激活环境之后再继续后续操作。这里有个很常见的坑:如果忘了conda activate deepspeed,后面所有pip包都装进了系统环境,实际运行Python时却用的是conda环境,最终必然出现ModuleNotFoundError: No module named 'torch'。
3. 安装PyTorch GPU版
3.1 区分CPU版和GPU版
在Windows上装PyTorch,最大的坑是默认版本问题。如果直接跑pip install torch,大概率会从PyPI拉到一个不带CUDA的CPU版本。CPU版本虽然能跑,但torch.cuda.is_available()永远返回False,DeepSpeed编译时也找不到CUDA工具链,整个方案直接废掉。
GPU版的PyTorch必须从PyTorch官方索引安装。核心命令是这个:
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121这里的cu121代表CUDA 12.1。我用的PyTorch版本是2.6.0,对应CUDA 12.1的wheel包,和DeepSpeed配合很稳定。
3.2 国内网络环境下的安装策略
如果你在内网或者网络不稳定,官方源可能拉不动大文件。我曾见过有人配了清华源,然后发现pip install torch装的还是CPU版——这是因为通用镜像源只提供CPU版wheel包,GPU版必须走PyTorch官方源。
折中做法是先用浏览器或者下载工具,从https://download.pytorch.org/whl/cu121/torch-2.6.0+cu121-cp310-cp310-win_amd64.whl拉到本地,再通过本地文件安装:
pip install C:\downloads\torch-2.6.0+cu121-cp310-cp310-win_amd64.whl pip install C:\downloads\torchvision-0.21.0+cu121-cp310-cp310-win_amd64.whl pip install torchaudio==2.6.0 --index-url https://download.pytorch.org/whl/cu121本地wheel安装的好处是稳定、可断点续传、还能确保版本完全匹配。我实际下载的时候发现这些wheel文件动辄2GB多,用下载工具比pip直连稳定得多。
3.3 验证PyTorch确实吃到了GPU
装完之后,别急着往后走,先跑一段验证代码:
python -c "import torch; print(torch.__version__); print(torch.cuda.is_available()); print(torch.version.cuda)"正常输出应该类似:
2.6.0+cu121 True 12.1如果torch.cuda.is_available()返回False,那就别继续往下走了,先把GPU环境修好。最常见的两个原因,一是驱动太旧不支持CUDA 12.1,二是torch还是CPU版。这时需要确认:
pip show torch看版本号里有没有+cu121字样。没有的话,卸载掉重装GPU版:
pip uninstall torch torchvision torchaudio -y然后再按之前的命令装GPU版。
4. 编译DeepSpeed:核心战场
4.1 先把源代码和依赖拉齐
环境准备好后,进入最关键的编译环节。首先从GitHub拉取DeepSpeed源码:
git clone https://github.com/microsoft/DeepSpeed.git cd DeepSpeed然后确保Python依赖安装完整,一般用pip install -r requirements/requirements.txt就能搞定。我在Windows上执行时发现这个文件里的依赖版本偏低,但不影响使用。真正重要的是下面两个编译包:
pip install ninja pip install setuptools>=64.0Ninja是并行构建工具,能大幅缩短编译时间;setuptools新版支持PEP 660,对源码安装有很多细节优化。
4.2 设置编译开关和环境变量
这是最核心的一步。DeepSpeed编译受环境变量控制,在Windows上必须手动指定。我在cmd里执行的是:
set DS_BUILD_OPS=1 set DS_BUILD_AIO=0 set DS_BUILD_UTILS=1 set DS_BUILD_FUSED_ADAM=1 set DS_BUILD_CPU_ADAM=1 set DS_BUILD_ONEADAM=1几个变量的含义我解释一下:
DS_BUILD_OPS=1:总开关,让DeepSpeed真正编译C++算子。不设这个的话,装完就是个空壳。DS_BUILD_AIO=0:禁用libaio异步I/O模块,这个模块在Windows上根本没有对应库,不关掉编译必挂。DS_BUILD_UTILS=1:编译工具类算子,很多功能依赖这层。DS_BUILD_FUSED_ADAM=1和DS_BUILD_CPU_ADAM=1:显式开启融合优化器算子。这是大模型训练时真正省显存、提速度的关键部件。
如果你是在PowerShell里操作,语法略有不同:
$env:DS_BUILD_OPS='1' $env:DS_BUILD_AIO='0' $env:DS_BUILD_UTILS='1' $env:DS_BUILD_FUSED_ADAM='1' $env:DS_BUILD_CPU_ADAM='1' $env:DS_BUILD_ONEADAM='1'4.3 正式编译与安装
环境变量设置好之后,在“x64 Native Tools Command Prompt for VS 2022”里执行:
python setup.py build_ext --inplace注意这里用build_ext --inplace而不是直接pip install -e .。前者的好处是会先编译算子,并且在当前目录生成编译产物,方便第一时间发现哪个算子编译失败。我第一次执行时,就卡在fused_adam算子上报了一堆MSVC语法错误。排查之后发现是缺少Windows头文件支持,后来通过更新到最新源码分支解决。
build_ext成功之后,再正式安装:
pip install -e .-e表示可编辑安装,源码改动后不用重新安装就能生效,调试时特别方便。
编译过程会花掉一段时间,具体取决于CPU核数和内存。我机器的配置是16核,编译耗时大约10分钟。编译期间要看到大量ninja: build completed这类信息才算正常。
4.4 编译完成后的验证
编译完成后,用ds_report验证一下DeepSpeed的算子是否真正可用:
ds_report正常情况下,应该在输出里看到cpu_adam、fused_adam等算子的状态显示为[OKAY]。如果显示[WARNING]或[FAILED],说明对应算子编译有问题,需要回头检查。
另外,直接跑一段Python验证也能看出问题:
python -c "import deepspeed; print(deepspeed.__version__)" python -c "from deepspeed.ops.adam import DeepSpeedCPUAdam; print('CPUAdam OK')"如果第二行没有报错,说明CPUAdam算子编译成功,整个DeepSpeed的编译链路基本就通了。
5. 实测跑通一个GPU训练例子
5.1 准备测试脚本
编译通过只是第一关,真正让DeepSpeed和PyTorch在GPU上协同工作,还需要实测。我准备了一个最小化的ZeRO-2训练脚本,来验证整个链路是否真的可用。
import torch import deepspeed from torch import nn class TinyModel(nn.Module): def __init__(self, dim=512): super().__init__() self.net = nn.Sequential( nn.Linear(dim, dim * 2), nn.ReLU(), nn.Linear(dim * 2, dim) ) def forward(self, x): return self.net(x) ds_config = { "train_batch_size": 16, "gradient_accumulation_steps": 1, "optimizer": { "type": "Adam", "params": {"lr": 1e-4} }, "zero_optimization": { "stage": 2 }, "fp16": { "enabled": True } } model = TinyModel().cuda() model_engine, optimizer, _, _ = deepspeed.initialize( model=model, model_parameters=model.parameters(), config=ds_config ) inputs = torch.randn(8, 512).cuda() labels = torch.randn(8, 512).cuda() for step in range(100): outputs = model_engine(inputs) loss = nn.MSELoss()(outputs, labels) model_engine.backward(loss) model_engine.step() if step % 20 == 0: print(f"step {step} loss {loss.item():.4f}")这里有几个细节说明一下:
model_engine = deepspeed.initialize(...)返回的对象封装了原生PyTorch模型,完全替代了原来的model。前向、反向、更新全都走model_engine的接口。- ZeRO-2会把优化器状态和梯度分片到多卡,但单卡上开启stage 2也能正常跑,可以用来验证通信原语是否可用。
- 开启
fp16要求模型支持自动混合精度,测试模型默认支持,直接跑没问题。
5.2 观察显存与速度
跑这个脚本的时候,我习惯在另一个终端开着nvidia-smi -l 1监控显存和GPU利用率。
我实测时显存大约占用了2.3GB左右,对于一个512维小模型来说很正常。关键是这个过程能证明两点:第一,DeepSpeed的初始化没有报错,说明算子和通信库真的编译成功了;第二,GPU利用率能看到明显的周期性波动,说明训练在真正调用CUDA核心。
如果你跑起来之后完全看不到显存占用变化,多半是fp16没生效,或者模型压根没在GPU上。这时检查一下torch.cuda.is_available(),再确认模型在model_engine.backward之前的输入都在cuda()上。
5.3 踩过的性能坑
测试过程中最容易让人误解的一个现象是:第一次前向时耗时特别长,甚至比CPU还慢。这是正常的,因为DeepSpeed在第一次前向时会做大量的初始化工作,比如分配GPU内存、建立通信组、预热融合算子。多跑几个step之后,速度会趋于稳定。
还有一个坑是train_batch_size必须能被gradient_accumulation_steps × 实际batch size整除。我一开始配了train_batch_size=16,但每个step实际喂了inputs的batch size是8,导致DeepSpeed在内部计算梯度累积步数时报错。后来把train_batch_size改成8,或者让inputs的batch size翻倍,才正常。
6. 常见问题速查表
整个过程中我整理了最有代表性的几类报错和排查思路,在这里一并列出,方便你快速对照:
| 报错/现象 | 根本原因 | 处理方式 |
|---|---|---|
ModuleNotFoundError: No module named 'torch' | Python环境没激活或torch装到了其他环境 | conda activate deepspeed后重装torch |
error: could not find a version that satisfies the requirement torch | 从PyPI默认源拉包,找不到合适版本 | 使用--index-url https://download.pytorch.org/whl/cu121 |
torch.cuda.is_available()为False | torch是CPU版或NVIDIA驱动过旧 | 卸载后重装GPU版;更新驱动 |
Microsoft Visual C++ 14.0 or greater is required | 缺少VS C++ Build Tools | 安装VS 2022 Build Tools并勾选“使用C++的桌面开发” |
nvcc: No such file or directory | CUDA环境变量未配置 | 将CUDAPATH\bin加入系统Path |
编译时DS_BUILD_AIO相关报错 | Windows无libaio支持 | 设置DS_BUILD_AIO=0 |
ds_report显示cpu_adam不可用 | 编译时算子没编成功 | 检查编译日志,确认MSVC工具链环境,重跑build_ext |
failed to initialize nvml: GPU access blocked by the operating system | 使用了WSL但GPU透传未配置,或驱动不支持 | 检查Windows侧驱动,确保WSL版本和NVIDIA驱动匹配 |
attn_implementation="torch" is not supported | 某些模型推理框架和torch版本不兼容 | 升级或降级torch,按框架文档设置正确的attention实现 |
这里重点提醒一下WSL的情况。如果你是在Windows Subsystem for Linux里编译DeepSpeed,遇到NVML初始化失败的概率比原生Windows高得多,因为WSL2的GPU透传依赖Windows宿主机驱动版本和WSL的协同支持。我最后选择直接在原生Windows上编译,省掉了很多麻烦。
7. 最后的几点实操建议
编译DeepSpeed这件事,说难也难,说简单也简单,关键是把工具链版本对齐。我在这个项目里最大的体会是,网上关于Windows编译DeepSpeed的教程少且零散,很多人试了几天就放弃了。但只要你忍下性子,把CUDA、VS、PyTorch和DeepSpeed的版本关系理清楚,整个过程其实是可以复现的。
还有一个建议:如果你只是短期做实验,不想维护本地编译环境,也可以考虑租一台Linux GPU服务器跑任务,Windows本地只做代码编辑和调试。但如果你跟我一样,必须长期在Windows环境下做训练和微调,那花一个下午把DeepSpeed编译通,是非常值得的投资。之后每次创建新环境,只需要复用这套流程就能快速装好。
最后分享一个小技巧:你把编译好的DeepSpeed相关wheel包留存一份,下次换机器或者重装系统时,直接pip install本地wheel,可以跳过漫长编译。我第二次配置时,整个环境搭建从原来的四小时压缩到了二十分钟以内,效率提升非常明显。