如果你在 PyCharm 里跑 StyleGAN2,大概率会在第一次编译或者 import 的时候撞上一堵墙:CUDA 扩展编译失败。这堵墙我撞过不止一回,而且每次都长得不一样——有时候是 ninja 报错,有时候是 GBK 编码,有时候是 MSVC 版本不兼容,还有时候是 PyCharm 这边环境变量没传进去,导致 cl.exe 根本找不到。
这篇文章就是把我这几轮折腾的完整记录复盘一下。不光是贴报错和解决方案,也会把背后的原理讲清楚:StyleGAN2 的 CUDA 扩展到底在编译什么,为什么 PyCharm 里尤其容易出问题,以及一套能稳定跑通的 Windows 环境怎么搭。如果你正被这类错误卡住,或者后面准备在 Windows 上跑其他需要自定义 CUDA 算子的仓库(比如 StyleGAN3、一部分 NeRF 实现),这篇文章可以直接当排查手册用。
1. 先搞懂一件事:StyleGAN2 的 CUDA 扩展到底在编译什么
1.1 这段编译是怎么被触发的
很多人在 PyCharm 里下载了 StyleGAN2-ADA-PyTorch 的代码,装好 torch 之后直接跑 python train.py,然后就看见终端里滚过一大片 Building extension 相关的日志,接着报错。这里有个关键认知:StyleGAN2-ADA 这个仓库里有几个性能敏感的操作不是用纯 PyTorch 实现的,而是用 C++ 和 CUDA 写成的自定义算子,比如 upfirdn2d(上采样和滤波)、bias_act(带泄露的激活函数)、grid_sample_gradfix(可微网格采样)等。
这几个算子在仓库的 stylegan2_ada_pytorch/ 对应目录下以 .cu 和 .cpp 文件存在。第一次使用的时候,会通过 setup.py 调用 setuptools 加 ninja 去把它们编译成 .pyd(Windows 下的扩展模块)文件,之后的 import 阶段就会直接加载编译产物。
我的建议是:遇到编译错误先别急着去改代码,因为你可能连“是哪一步挂的”都未必清楚。整个编译链条大致是这样:
- 第一步:
setup.py找当前 Python 环境对应的 PyTorch C++ 头文件,比如<ATen/ATen.h>。 - 第二步:调用
ninja这个构建工具,按照 build 规则逐个编译.cpp和.cu。 - 第三步:
nvcc(CUDA 编译器)负责编译.cu,MSVC 的cl.exe负责编译.cpp。 - 第四步:链接生成
.pyd文件,放到扩展缓存目录里。
这四个环节任何一个出问题,你看到的都是“CUDA 扩展编译错误”,但真正的原因可能差得很远。
1.2 为什么 PyCharm 里特别容易翻车
这里我不太想一笔带过,因为这是很多人没意识到的一个点。你直接在“终端”里跑同样的命令可能能过,但在 PyCharm 里一跑就挂,原因往往不是代码问题,而是 PyCharm 对环境的隔离方式。
具体来说有三个坑:
第一个是“运行按钮”和“终端”使用的是不同的环境变量来源。你用 PyCharm 右上角那个绿色按钮跑脚本时,继承的是 PyCharm 进程启动时从系统读取的环境变量。如果你中途去系统设置里改了 CUDA_PATH 或者 PATH,没有完全退出并重启 PyCharm 的话,新环境变量不会生效。
第二个是 PyCharm 的 Terminal 工具窗口默认用的是系统 shell,但如果你在项目里选了某个 Conda 环境作为解释器,Terminal 里不一定自动激活这个环境。这时候你在终端里 python 可能是另一个解释器,编译出来的东西跑到别的环境里去了。
第三个是 PyCharm 默认不会加载 Visual Studio 的编译环境。MSVC 的 cl.exe 不像普通的 exe 那样直接出现在 PATH 里,它需要先执行 vcvars64.bat 来设置一堆环境变量。PyCharm 启动时不会主动执行这一步,所以你在 PyCharm 里直接跑 setup.py 时,ninja 经常报 “不能找到 cl.exe”。
搞明白这件事之后,你再看那些零散的网上的解决方案,就不会盲目试了。很多人说的“在系统环境变量里加上 C:\Program Files\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133\bin\Hostx64\x64”,本质上就是为了让 cl.exe 能被找到。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境选型:这一步做好了才谈得上编译通过
2.1 版本匹配不是玄学,是工程问题
StyleGAN2 的仓库是 2020 年左右发布的,它适配的 PyTorch 版本停留在 1.7~1.9 左右。但是你不能真的就装一个老掉牙的 PyTorch 然后指望它在 2026 年的显卡上还能跑。这里有个现实矛盾:太新的 PyTorch 可能改了一些 C++ API,导致老仓库的自定义算子编译不过;太老的 PyTorch 又对应老 CUDA,可能不认你的新显卡架构。
我实测下来比较稳的组合有这几套,你可以按自己的显卡选:
| 显卡架构 | 推荐 CUDA Toolkit | 推荐 PyTorch | 推荐 Python | 备注 |
|---|---|---|---|---|
| Turing(20系)/ Ampere(30系) | CUDA 11.1~11.6 | torch 1.9.0 / 1.12.1 | 3.8 / 3.9 | 最省心,社区验证最多 |
| Ampere(30系)/ Ada(40系) | CUDA 11.8 | torch 2.0.1 | 3.9 / 3.10 | 兼顾新特性和兼容性 |
| Ada(40系) | CUDA 12.1 | torch 2.1+ | 3.10 / 3.11 | 需要额外处理 Arch List,不推荐新手 |
关于 Python 版本,我个人的建议是优先用 Python 3.9。原因不是玄学,而是老仓库里有些代码会用到 Python 3.9 之后才可能被移除的旧 API,而且很多 conda 包装的是老 CUDA 版本,对新 Python 支持不好,来回折腾的运气成本很高。
你还需要理解一个容易混淆的点:pip install torch==1.12.1+cu116 这种安装方式,虽然会带上一个完整的 CUDA runtime,但不包含 CUDA Toolkit 里的编译器 nvcc。编译自定义 CUDA 算子时,系统里必须单独装一个对应版本的 CUDA Toolkit,光靠 pip 装的 torch 是不够的。这一点是多数人编译报错的根源,因为 nvcc 根本找不到,然后编译器直接退出。
2.2 多版本 CUDA 并存时的环境变量策略
很多人电脑里不止一个 CUDA,今天装了个 11.8,明天装了个 12.1,或者之前为了 OpenCV 装过 10.2。这些版本如果都写入同一个 PATH,nvcc -V 显示的可能就不是你当前想要的那个版本。
我自己的习惯是不用系统全局的 CUDA_HOME,而是为每个项目动态设置环境变量,在 PyCharm 的 Run/Debug Configurations 里单独配置 Environment variables,这样就不会影响系统里其他项目。
具体配置如下面的例子:
text复制CUDA_PATH=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.8
CUDA_HOME=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.8
PATH=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.8\bin;%PATH%
TORCH_CUDA_ARCH_LIST=8.6
其中 TORCH_CUDA_ARCH_LIST 这个变量很多人不熟悉,这里多说一句:它告诉编译器你的 GPU 是什么架构。30 系是 8.6,40 系是 8.9,20 系是 7.5。如果不设置,老版本的 PyTorch 会尝试编译一个默认列表里的架构,里面很可能没有你的卡,结果就是编译能过,但运行时报 “no kernel image is available for execution on the device”。
3. 完整实操流程:一步步把 CUDA 扩展编译出来
3.1 环境准备:从零搭一套能编译的 Windows 环境
假设你现在是全新的 Windows 系统,要跑 StyleGAN2-ADA-PyTorch,我的建议步骤是这样的。
先装 Visual Studio 2022 或者 2019,安装时一定要勾选“使用 C++ 的桌面开发”工作负载。如果你电脑空间紧张,可以只装 “VS Build Tools”,不需要装整个 IDE。但安装完必须确保 cl.exe 能被找到。
然后是 CUDA Toolkit。我建议装 11.8,兼容性比较好。安装完成后验证一下:
bash复制nvcc -V
这里有个小知识:nvidia-smi 显示的 CUDA 版本是驱动支持的最高版本,它不代表你当前安装了哪个版本的 Toolkit。所以即使 nvidia-smi 显示 12.6,你也完全可以装一个 11.8 的 Toolkit 用于编译,两者不冲突。只要 nvcc -V 输出你预期版本即可。
再创建 conda 环境并安装依赖:
bash复制conda create -n stylegan2 python=3.9
conda activate stylegan2
pip install torch==1.12.1+cu116 torchvision==0.13.1+cu116 --extra-index-url https://download.pytorch.org/whl/cu116
pip install click requests tqdm pyspng ninja imageio imageio-ffmpeg scikit-image
ninja 一定要装,因为官方仓库的 setup.py 默认优先用 ninja 作为构建后端,而且它比 setuptools 自带的 build_ext 快很多。
3.2 编译前的基础验证清单
别急着跑训练脚本,先把几个关键点验证一遍,能省下好几轮无效编译。
第一,验证 PyTorch 能不能看到 CUDA 设备。
python复制import torch
print(torch.__version__)
print(torch.cuda.is_available())
print(torch.version.cuda)
print(torch.cuda.get_device_name(0))
如果 torch.cuda.is_available() 返回 False,后面所有编译都没意义,因为 StyleGAN2 会直接放弃编译 CUDA 扩展。这种情况优先检查 PyTorch 版本是不是带了 CUDA 的 wheel,比如 +cu116 这种后缀。
第二,验证系统的编译工具链是否完整。
在命令行里执行:
bash复制cl
如果在普通 cmd 里提示“不是内部或外部命令”,说明 MSVC 环境没加载。你需要打开 “x64 Native Tools Command Prompt for VS 2022”,在这个窗口里再执行 cl 才有效。
这个“x64 Native Tools Command Prompt”是从开始菜单启动的,它会自动执行 vcvars64.bat,加载 MSVC 的编译环境。在 PyCharm 里跑编译之前,你可以先手动在这个窗口里跑一次完整的 setup 流程,先排除工具链的问题。
第三,验证 nvcc 在哪里。
bash复制where nvcc
注意,如果 where nvcc 显示的路径不是你想用的 CUDA 11.8,而是别的版本目录,那就需要在环境变量里调整 PATH 的顺序。
3.3 实际编译:我的失败现场和解决过程
设置好之后,进入 StyleGAN2-ADA-PyTorch 的仓库目录,执行:
bash复制python setup.py install
或者更轻量一点,直接尝试 import 项目核心模块,触发编译:
python复制import dnnlib
import torch_utils
我第一次在 PyCharm 里跑的时候,报错信息很长,核心是 ninja: build stopped: subcommand failed。这个错误信息本身没有任何排查价值,真正的原因在它上面几十行。
由于 ninja 默认是并行编译的,出错的具体文件会非常多,建议先加一个环境变量关掉并行:
text复制MAX_JOBS=4
或者干脆在 setup.py 里把并行度降下来,让出错信息稳定地指向某一个文件。第二次跑的时候,我的报错变成了 fatal error C1083: 无法打开包括文件: "ATen/cuda/CUDAContext.h"。这个错误就很典型了——说明 PyTorch 的头文件路径没有被正确传给编译器。原因是 PyCharm 运行 setup.py 时,使用的 Python 解释器路径和当前激活的 conda 环境不是同一个。PyTorch 的头文件在 site-packages/torch/include 下面,如果 python 解释器不对,自然找不到。
解决方法是:在 PyCharm 的 Settings -> Project -> Python Interpreter 里,明确选中你创建的 stylegan2 环境,然后用 PyCharm 自带的 Terminal 窗口执行 conda activate stylegan2,再跑 setup。
第三次的报错变成了 UnicodeDecodeError: 'gbk' codec can't decode byte ...。这是 Windows 中文系统特有的坑。Ninja 输出的是 UTF-8 编码的日志,但 Python 在 Windows 上读取子进程输出时默认用了 GBK 解码。解决方法是设置编码环境变量:
bash复制set PYTHONUTF8=1
或者在 PyCharm 的配置里把这个变量加进去。
终于,第四次编译通过,终端打印出了 Building extensions... done,然后我可以正常 import dnnlib 了。整个过程前后花了大半天,但大部分时间都浪费在前面两次无效尝试上,真正的问题其实只有三个:解释器选错、环境变量没传递、编码乱码。
3.4 编译产物在哪里
编译成功之后,扩展文件会被放到 stylegan2_ada_pytorch/ 目录下的 torch_utils/ops/ 相关子目录里,文件名类似 upfirdn2d.pyd。这些 .pyd 文件是二进制模块,如果之后换了 PyTorch 版本或者换了 CUDA 版本,最好把缓存清理掉重新编译。
清理缓存的方式是删除目录下的 build 文件夹以及所有以 .pyd 结尾的文件,重新跑一次 setup。这个操作要记住,因为很多人改完环境之后发现 import 还是报错,其实是因为旧扩展还在缓存里没被覆盖。
4. 高频错误对照速查表
下面的错误我都实际踩过,或者排查过,整理出来供你按图索骥。
4.1 ninja: build stopped: subcommand failed
这个错误信息太通用,上面说过了,真正的信息在上面。看到这个报错,先不要急着搜它本身,而是往上翻日志找第一个出现 error 或 fatal error 的行。常见的原因包括:缺少 MSVC、缺少 CUDA 头文件、路径有中文、磁盘空间不足。
一个高效的做法是把完整日志重定向到一个文件里看:
bash复制python setup.py install 2>&1 | tee build.log
在 Windows 的 cmd 里没有 tee,可以直接用重定向:
bash复制python setup.py install > build.log 2>&1
然后打开 build.log 搜索第一个 error。
4.2 C1083: 无法打开包括文件: "ATen/cuda/CUDAContext.h"
这个问题的核心是 PyTorch include 路径没有传给编译器。可能的原因有三个:一是当前 Python 解释器里根本没安装 torch;二是 PyTorch 版本太老,头文件结构变了;三是编译过程没有在预期的环境下运行。
解决方案是检查 import torch 的文件路径是否和 setup.py 使用的一致,可以在终端里先运行:
bash复制python -c "import torch; print(torch.__file__)"
看输出路径是不是在当前环境下的 site-packages 里。如果输出在其他环境的路径下,说明之前运行 PyCharm 时没有正确激活环境。
4.3 UnicodeDecodeError: 'gbk' codec can't decode byte
这个错误在中文 Windows 系统上很常见,因为 Python 默认编码是 utf-8 但 locale 是中文编码。Ninja 作为子进程输出日志时,Python 尝试用系统默认编码去解码它,遇到非 GBK 字符就崩了。
解决方案很简单,设置环境变量 PYTHONUTF8=1,让 Python 以 UTF-8 模式运行子进程,或者在 PyCharm 的运行配置里勾选“Emulate terminal in output console”。
4.4 Unsupported gpu architecture 'compute_86'
这个报错一般出现在你用的 PyTorch 版本比较老,而显卡比较新时。老版本的 PyTorch 自带的编译脚本不认识 compute_86 这种新架构。
解决方法是手动设置 TORCH_CUDA_ARCH_LIST,值为 8.6(30 系)或者 8.9(40 系)。注意 40 系如果安装的 CUDA Toolkit 低于 11.8,那么即使设置了 8.9 也可能编译不出来,因为编译器本身不支持这个架构。这时候同时更新 CUDA Toolkit 版本。
4.5 Windows SDK 和 MSVC 版本冲突:MSB8040、D8016
Visual Studio 2019 和 2022 的某些版本在编译含有 /std:c++14 的项目时会有一些新警告被当作错误。如果你看到 error MSB8040 或者 D8016 这种编号,通常是“Spectre 缓解库”没装,或者 /RTC1 和 /O2 冲突。
遇到 MSB8040,去 Visual Studio Installer 里把“C++ 的 Spectre 缓解库”装上即可。D8016 则通常在设置 CMAKE_MSVC_RUNTIME_LIBRARY 时出现,你可以在 setup.py 里强行加入 /d2SSAOptimizer- 之类的参数绕过,但更推荐直接安装 Visual Studio 2022 的 Build Tools 最新版本。
4.6 运行期报错:CUDA error: no kernel image is available for execution on the device
这个错误很容易让人以为是编译失败了,其实编译是成功的,只是编译出的 kernel(SASS 或者 PTX)不匹配当前显卡。原因就是前面说的 TORCH_CUDA_ARCH_LIST 没有正确设置,导致编译时只针对某个特定的 compute capability 生成了 SASS,没有为你的显卡架构生成对应代码。
解决方案:设置 TORCH_CUDA_ARCH_LIST 为你的显卡架构,并加 +PTX 后缀以便 JIT 编译到新架构:
text复制TORCH_CUDA_ARCH_LIST=8.6+PTX
加了 +PTX 之后编译产物会包含 PTX 中间表示,运行时会再针对当前显卡做一次 JIT 编译,虽然首次加载会慢一点,但兼容性更好。
5. 一些值得记住的避坑心得
从这套流程走过来之后,我个人的几个体会越来越深。
第一个体会是:老代码真的没必要非用最新版环境。很多人跑 StyleGAN2 失败,不是环境配置有什么高级问题,而是装了一个最新版 PyTorch + 最新版 Python,然后拿一个 2020 年的仓库往上面凑。这种情况下报错是正常的,不报错才是运气好。技术没问题,错的是组合。
第二个体会是:Windows 下跑这类仓库,尽量把“编译环境”和“运行环境”分开考虑。编译环境需要 VS + CUDA Toolkit,运行环境只需要 PyTorch + CUDA 驱动。很多人只需要运行,却被编译整崩溃了。如果条件允许,WSL2(Windows 子系统 Linux)里跑这类仓库通常比 Windows 原生环境省心不少和编译环境开箱即用。如果只能在 Windows 里做,那上文提到的“x64 Native Tools Command Prompt”就是你的爸爸级工具。
第三个体会是:PyCharm 不是万能的,它把环境隔离做得太好,有时候反而成了障碍。我的习惯是第一次编译一定在系统命令行或者 PyCharm Terminal 里完成,确认成功后再回到 PyCharm 的 Run 窗口里去跑训练。因为 Run 窗口不会自动加载 VCVars 环境,第一次就在 Run 窗口里编译大概率翻车。
最后一个是我个人踩过的小坑:项目的绝对路径里不要有中文、不要有空格。虽然现在很多库都兼容路径空格了,但 StyleGAN2 这种老代码里某些文件读写逻辑并不严谨,路径带了中文之后会在编译或者数据加载时报一些很诡异的错误。如果你的用户名是中文,建议把项目直接放到 C:\sg2\ 这种纯英文路径下,能避免一大半没必要的烦恼。
接下来直接跑你的第一个训练任务吧。如果中途又遇到别的报错,建议先搜一下具体报错信息,不要搜“stylegan2 cuda error”这种大而全的关键词,越具体越容易找到真实的解决方案。
