ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

CUDA11.3配cuDNN装PyTorch:WinError1114排错

CUDA11.3配cuDNN装PyTorch:WinError1114排错 CUDA 11.3 配 cuDNN 再装 torch 这条路线我前后至少走过十来次有在自己机器上装的也有远程帮别人排错的攒下来的坑基本能凑成一本小册子。这篇就把整条链路从头到尾拆一遍从nvidia-smi里那个版本号到底能不能信到 cuDNN 压缩包该往哪三个文件夹里丢再到 pip 装 torch 时那条命令为什么照着网上抄会翻车最后落到OSError: [WinError 1114] 动态链接库(DLL)初始化例程失败这个几乎人人都会撞上的报错。不管你是第一次配深度学习环境的新手还是换了台机器要重装一遍的老手按这个顺序过一遍能省下好几个晚上。我把内容分成两条主线一条是装一条是为什么这么装。只讲怎么点下一步的教程到处都是但真正卡住人的往往是版本对应关系和某个细节被省略了。所以凡是涉及参数选择的地方我都会把理由说清楚你以后遇到 CUDA 11.6、11.8 也能自己推。1. 先把版本关系理清楚CUDA、cuDNN、torch 到底谁管谁1.1 三个东西各自的角色别被 nvidia-smi 里的版本号骗了刚接触这块的人最容易混淆的就是三个版本号驱动版本、CUDA Toolkit 版本、以及 torch 编译时链接的 CUDA 版本。它们不是一回事也不会自动保持一致。先说你显卡驱动。驱动是操作系统和显卡之间的翻译层它本身内置了一套能支持到某个上限的 CUDA 运行时接口。你在命令行敲nvidia-smi右上角会显示类似CUDA Version: 11.3的字样注意这个数字的含义是我这版驱动最高支持到 CUDA 11.3 的运行时它既不代表你已经装了 CUDA 11.3也不代表你只能装 11.3。它就是个天花板提示。然后是 CUDA Toolkit。这是真正意义上的开发包里面包含nvcc编译器、CUDA 运行时库cudart64_113.dll这类、以及 cuBLAS、cuFFT 等加速库。只有装了它nvcc -V才有输出你才能编译 CUDA 扩展比如自定义算子、或者需要现场编译的库。最后是 cuDNN。它是建立在 CUDA 之上的深度神经网络原语库卷积、池化、归一化、RNN 这些算子的高性能实现都在里面。它不提供编译器只是一堆头文件加 dll/lib。它的版本必须严格对应 CUDA 版本比如 cuDNN 8.2.1 的 Windows 包就是绑 CUDA 11.3 的。注意真正决定torch.cuda.is_available()结果的不是你有没有装 CUDA Toolkit而是你装的 torch wheel 是哪个版本、以及驱动版本够不够。这一点很多人到报错那天才反应过来。1.2 CUDA 11.3 该配哪个 cuDNN、哪个 torch一张对齐表下面这张表是我自己反复验证过的组合可以直接抄。环境是 Windows x64显卡是 RTX 30 系或者更早的图灵卡。组件推荐版本说明显卡驱动465.89 及以上低于这个版本装不了 CUDA 11.3 的运行时CUDA Toolkit11.311.3.0/11.3.1主版本锁死补丁号不敏感cuDNN8.2.18.2.0 也行必须是 for CUDA 11.x 的那个包Python3.9 / 3.103.9 最稳3.11 及以上直接劝退torch1.10.1cu113 / 1.11.0cu113 / 1.12.1cu113三选一看你要不要新算子torchvision0.11.2cu113 / 0.12.0cu113 / 0.13.1cu113必须和 torch 一一对应torchaudio0.10.1cu113 / 0.11.0cu113 / 0.12.1cu113用不到音频也要装上对齐对应关系再强调一遍torch 1.10.1 只能配 torchvision 0.11 系列torch 1.11.0 配 0.12.0torch 1.12.1 配 0.13.1。这三个包是同一套 CI 出来的版本号错位最常见的结果就是import torchvision时报某个算子找不到符号或者干脆 import 阶段就炸。1.3 为什么我建议把 11.3 这条线锁死而不是追新有人会问现在都出到 CUDA 12 了为什么还折腾 11.3原因很朴素你手上的代码和模型可能就是为这条线写的。很多两年前开源的检测、分割、语音项目setup.py里写死了-gencode archcompute_86,codesm_86依赖的第三方扩展是基于 CUDA 11.x 编译的。你用 12.x 的 nvcc 去编译链接阶段就会报 ABI 不兼容。还有一些老版本的 apex、老版本的 DeepSpeed在 CUDA 11.3 上有大量人验证过的组合换成 12 之后你要自己当第一个吃螃蟹的人。另外从 wheel 覆盖率来看官方为 cu113 提供了完整的 Windows wheel覆盖 torch 1.10 到 1.12 所有小版本Python 3.7 到 3.10 都有。这个组合的确定性最高出错的时候网上能搜到答案的概率也最大。环境搭建这件事稳比新重要得多。2. 装之前的环境体检三分钟决定你要不要动系统级驱动2.1 nvidia-smi 输出怎么读驱动够不够一算就知道第一步永远是打开命令行敲nvidia-smi。如果提示不是内部或外部命令说明驱动压根没装或者没进 PATH先去官网把驱动装上别急着往下走。正常输出里你要盯三个地方左上角是驱动版本号中间是显卡型号和显存右上角是 CUDA Version。以驱动 465.89 为例右上角会显示 11.3意味着装 CUDA 11.3 刚好卡线。如果是 460.xx 的驱动右上角显示 11.2这时候你装 CUDA 11.3 的运行时就会报初始化失败——因为运行时要求的最低驱动版本你不满足。常见 CUDA 版本与 Windows 最低驱动版本的对应关系大致是这样记住个大概就够CUDA 运行时版本Windows 最低驱动11.0451.2211.1456.3811.2460.8211.3465.8911.5496.1311.6511.2311.7516.0111.8522.06注意这是最低驱动比它新是完全没问题的向下兼容。比如你驱动是 536.xx右上角显示 CUDA 12.2照样能跑 CUDA 11.3 的程序。真正会出问题的是反过来——驱动太旧。2.2 显卡算力与 CUDA 11.3 的兼容边界第二个要确认的是显卡的计算能力compute capability。CUDA 11.3 支持的算力范围是从 3.5 一直到 8.6基本覆盖了从 Maxwell 到 Ampere 的所有消费卡。RTX 30 系是 8.6RTX 20 系是 7.5GTX 10 系是 6.1都在范围内。要小心的有两种卡。一种是特别老的 Kepler 架构GTX 700 系列及以前算力 3.0CUDA 11.x 已经不再支持装上了也编译不出对应架构的 kernel。另一种是算力很新的卡比如 H100 是 9.0CUDA 11.3 的 nvcc 不认识这个架构-archsm_90会直接报错。怎么查自己的算力最省事的办法是装完驱动后跑一下nvidia-smi -q虽然它不直接给算力值但给了型号去官方算力对照表一查就有。也可以用deviceQuery这个小工具它藏在 CUDA 安装目录的extras\demo_suite里编译后运行会直接打印CUDA Capability Major/Minor version number。2.3 Python 版本先定下来再谈装 torch这是我见过最多人忽略的一环。很多人 conda 建环境时随手conda create -n pytorch python3.11然后开始pip install torch1.10.1结果 pip 告诉你找不到匹配的分发。因为 torch 1.10 的 wheel 只覆盖到 Python 3.9。具体边界torch 1.10 支持 Python 3.6 到 3.9torch 1.11 和 1.12 支持到 3.10Python 3.11 要等到 torch 2.0.1 才有官方 wheel。所以你如果铁了心要用 cu113 这条线Python 版本就老实选 3.9 或者 3.10。我个人的偏好是 3.9。原因一是很多老项目在 3.9 上测试最充分二是 3.10 有个别包比如某些版本的 onnx在 Windows 上装起来会找你要编译工具链多出一堆事。新建环境的命令写清楚conda create -n pytorch113 python3.9 -y conda activate pytorch113环境建好后先python -V确认一下确实是 3.9然后赶紧把 pip 升级到较新版本老版本 pip 对--extra-index-url的解析有时候会挑错源装出一堆非预期的东西。3. CUDA 11.3 与 cuDNN 的手动安装实操3.1 安装包怎么选local 还是 network组件勾哪些官方下载页上每个 CUDA 版本都提供两种安装器network 版体积小安装时联网拉取local 版一两个 G全部内容打包在里面。我强烈建议下 local 版原因很实际——network 版在国内网络环境下中途断一次就得从头来而且失败时的报错常常是某个组件安装失败你不知道是网络问题还是权限问题。运行 exe 后它会先自解压到一个临时目录然后进入组件选择界面。默认勾选的选项里有几项要手动调整CUDA Runtime必装。CUDA Development必须勾nvcc和头文件都在这里缺了它你连nvcc -V都没有。Visual Studio Integration如果你机器上有 Visual Studio 2017/2019建议勾上方便后续编译扩展。没装 VS 的话勾了也没用会提示找不到 VS。Driver components这一步是关键。如果你已经装了比 465.89 更新的驱动千万不要勾这一项。勾了它会尝试降级或覆盖你的显卡驱动过程中鼠标会闪烁、屏幕会黑一下万一驱动装失败你得进安全模式救。Documentation / Samples按需我一般只勾 Samples方便拿deviceQuery验证。安装路径保持默认的C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.3就行路径里有空格没关系但别改成中文路径。3.2 cuDNN 压缩包的正确复制姿势cuDNN 从 8.x 开始不再提供 exe 安装器除了给特定框架的下载下来是个 zip 包名字类似cudnn-11.3-windows-x64-v8.2.1.32.zip。解压之后里面是bin、include、lib三个目录要做的就是把这几个目录里的文件合并到 CUDA 安装目录对应的位置。具体三个动作把bin\cudnn64_8.dll复制到C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.3\bin把include\下的所有.h文件复制到...\CUDA\v11.3\include把lib\x64\下的cudnn.lib和cudnn64_8.lib复制到...\CUDA\v11.3\lib\x64复制的时候会弹管理员权限确认点继续。别偷懒只复制 dll——如果后面你要编译带 cuDNN 的自定义算子头文件和 lib 缺一个都会在链接阶段报错那时候再去翻会很烦。提示cuDNN 版本号选对很重要。CUDA 11.3 对应的是 cuDNN 8.2.x8.1.x 是给 11.2 的8.3.x 主要给 11.5 以上。下载页面上标着 for CUDA 11.x 的包是可以的但更保险的是认准 11.3 字样。3.3 环境变量与验证nvcc -V 之外还要看什么安装器一般会自动加环境变量但自动加的有时不完整尤其是多版本共存的时候。装完先手动检查这三项CUDA_PATH应该是C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.3PATH里应包含%CUDA_PATH%\bin和%CUDA_PATH%\libnvvp有些脚本会读CUDA_HOME建议也一并设成同样的路径设完之后必须重开命令行窗口环境变量才会生效。然后验证nvcc -V正常输出里会有Cuda compilation tools, release 11.3, V11.3.109这样的字样。接着去extras\demo_suite目录打开对应版本的 Visual Studio 命令行工具编译运行deviceQuery.exe看到Result PASS就算 CUDA 这边彻底通了。这里还有个细节lib\x64目录要不要加进 PATH现代版本的 CUDA 在 Windows 上通常不需要因为运行时 dll 都在bin里。但如果你用的是非常老的 torch wheel1.7 及以前它可能去lib\x64找某些库那时候补上就行。3.4 多版本 CUDA 共存的切换方式很多人机器上不止一个 CUDA比如 11.3 和 12.1 并存。共存本身没问题安装器会装到不同的v11.3、v12.1目录两者互不干扰。麻烦的是 PATH 顺序——谁在前面命令行里调用的nvcc就是谁。我自己的做法是不去动 PATH 顺序而是给每个版本写一个切换脚本。比如建一个use_cuda113.batecho off set CUDA_PATHC:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.3 set PATH%CUDA_PATH%\bin;%CUDA_PATH%\libnvvp;%PATH% echo CUDA switched to 11.3 nvcc -V要用哪个版本就先跑哪个 bat然后在这个窗口里做编译工作。这样不会把系统 PATH 搞得一团乱也能避免昨天还能编译今天就不行这种玄学问题。4. 安装 PyTorch命令怎么写才不出错4.1 pip 装的 torch 自带运行时这件事决定了安装策略这是个很重要但很少有人说清楚的认知官方发布的torch-1.11.0cu113-cp39-cp39-win_amd64.whl这个文件里已经打包了 CUDA 运行时和 cuDNN 的二进制文件。它们在site-packages\torch\lib下面你会看到cudart64_113.dll、cublas64_11.dll、cudnn64_8.dll等等。这意味着什么意味着只是运行 torch、跑推理和训练你其实不需要在系统里装完整的 CUDA Toolkit 和 cuDNN。系统级的 CUDA 只在两种情况下是必需的一是你要用nvcc编译 CUDA 扩展比如torch.utils.cpp_extension、apex、各种自定义算子二是某些库在运行时会去系统路径找特定 dll。理解这一点之后安装策略就清楚了如果你只是跑跑开源模型pip 一条命令就够如果你要自己写算子、编译扩展那才需要老老实实装 Toolkit 和 cuDNN。我见过有人为了装得完整先装一遍完整 CUDA又用 conda 装一遍 cudatoolkit再 pip 装一遍带 cu113 的 torch最后三套运行时互相打架import 阶段就崩了。4.2 cu113 路线的完整命令pip 与 conda 两条先说 pip 路线这是我最推荐的因为 wheel 直接对应官方编译版本没有中间商。以 torch 1.11.0 为例pip install torch1.11.0cu113 torchvision0.12.0cu113 torchaudio0.11.0 --extra-index-url https://download.pytorch.org/whl/cu113注意--extra-index-url和--index-url的区别。用--index-url会把主源完全替换成 PyTorch 的源好处是绝对不会装错版本坏处是其他依赖包比如 numpy、pillow也得从那儿拉速度慢。用--extra-index-url则是在默认源之外增加一个候选源pip 会挑满足版本约束的那个。我个人偏好后者配合国内镜像pip install torch1.11.0cu113 torchvision0.12.0cu113 torchaudio0.11.0 -i https://pypi.tuna.tsinghua.edu.cn/simple --extra-index-url https://download.pytorch.org/whl/cu113如果你想要更老的组合把版本换成 torch 1.10.1 torchvision 0.11.2 torchaudio 0.10.1 即可。再说 conda 路线。conda 装的是cudatoolkit注意它跟完整 Toolkit 不是一回事——它是运行时的精简裁剪版不包含nvcc。命令是这样的conda install pytorch1.11.0 torchvision0.12.0 torchaudio0.11.0 cudatoolkit11.3 -c pytorch -c conda-forgeconda 路线的好处是依赖解析更严格坏处是下载慢、有时候会把你的 numpy 版本改掉。而且如果之前 pip 装过 torchconda list和pip list里会同时出现两条 torch 记录加载的时候谁生效说不清这是后面 1114 报错的一大来源。注意千万不要 conda 装一遍再 pip 覆盖装一遍。要换路线就把环境删了重建conda env remove -n pytorch113干净利落。顺便说一句网上流传的各种一键命令。我见过不少人直接抄一条形如pip install torch2.11.0 torchvision0.26.0 torchaudio2.11.0 --index-url ...的命令版本号对不对先不说关键是养成习惯任何一条安装命令先去官方版本对应页面核对一遍三个包是不是成套的、这个版本是不是真的存在。如果版本不存在pip 会明确报ERROR: Could not find a version that satisfies the requirement torchxxx看到这个报错先回去查版本号别去怀疑网络。4.3 装完必做的四项验证装完别急着写模型先做四个检查这四行输出决定了后面所有的排查方向。import torch print(torch.__version__) # 期望1.11.0cu113 print(torch.version.cuda) # 期望11.3 print(torch.cuda.is_available()) # 期望True print(torch.cuda.get_device_name(0)) # 期望你的显卡型号四项里如果第一项打印出1.11.0cpu说明装成 CPU 版了直接重装。如果torch.version.cuda是None同理。如果is_available()是 False 但前两项都对那问题出在驱动或者 dll 加载上去第 5 章。再补一项验证 cuDNN 有没有正确挂上print(torch.backends.cudnn.version()) # 期望8201 之类的数字 print(torch.backends.cudnn.enabled) # 期望True4.4 离线安装与镜像源下载超时怎么办torch 的 cu113 wheel 在 Windows 上单个文件接近 2.5G网络不好的时候pip会卡在下载阶段或者下载到 99% 报超时。几个应对办法按推荐顺序排第一先手动把 wheel 文件下载下来。官方源地址是https://download.pytorch.org/whl/cu113/torch_stable.html这个页面上能列出所有 cu113 的 wheel 文件名找到对应你 Python 版本和系统的那个比如torch-1.11.0cu113-cp39-cp39-win_amd64.whl用浏览器或者下载工具拉下来。第二本地安装pip install torch-1.11.0cu113-cp39-cp39-win_amd64.whl pip install torchvision-0.12.0cu113-cp39-cp39-win_amd64.whl pip install torchaudio-0.11.0cu113-cp39-cp39-win_amd64.whl本地装的顺序无所谓但要注意三个包必须都从同一个来源下别一个从官方下、一个从某个镜像下版本尾号可能对不上。第三如果连浏览器都拉不动可以设置 pip 的超时和重试参数pip install torch1.11.0cu113 --extra-index-url https://download.pytorch.org/whl/cu113 --timeout 120 --retries 5超时时间从默认的 15 秒提到 120 秒重试次数提到 5 次对付偶发断流比较有效。5. 踩坑实录报错信息和它们的真实原因5.1 OSError WinError 1114c10.dll 初始化例程失败先把完整报错贴出来这个报错长这样OSError: [WinError 1114] 动态链接库(DLL)初始化例程失败。 Error loading C:\Users\24303\.conda\envs\pytorch\lib\site-packages\torch\lib\c10.dll or one of its dependencies.关键词是or one of its dependencies。这句话的意思是系统加载c10.dll的时候发现它依赖的某个更底层的 dll 加载失败了。所以问题大概率不在c10.dll本身而在它依赖的链条上。我按排查优先级给你排个序前三条能解决八成以上的情况第一条缺 MSVC 运行时库。torch 的 dll 是用 MSVC 编译的依赖VCRUNTIME140.dll、VCRUNTIME140_1.dll、MSVCP140.dll这几个。很多纯净系统或者精简版系统里没有。解决办法是装一个 Microsoft Visual C Redistributable for Visual Studio 2015-2022x64 版本装完重启命令行再试。这个是最常见的原因也是成本最低的排查动作一定先做。第二条torch 装了两份。前面提过 conda 和 pip 混装的问题。表现就是site-packages里有一份 torchconda-meta记录里又有一份两份版本不同加载时 dll 互相冲突。检查方法conda list | findstr torch pip list | findstr torch如果两边都列出 torch那这个环境基本废了最省事的做法是删环境重建。第三条多个 CUDA 版本的 dll 串了路径。如果你的 PATH 里同时有 CUDA 12.x 的 bin 目录和 CUDA 11.3 的 bin 目录加载器可能先找到了 12.x 的cudart64_12.dll或者别的库里同名但版本不同的 dll导致初始化失败。这时候临时把 PATH 简化只留 11.3 的路径试一次。如果前三条都不管用再往下看第四条用依赖查看工具定位。下载一个 Dependencies开源那个 dll 依赖分析工具把c10.dll拖进去它会列出所有依赖项和加载状态。标红的那个就是缺的。我遇到过缺libiomp5md.dll的情况那是 OpenMP 运行时的问题把torch\lib目录加入 PATH 或者补上对应文件即可。第五条杀毒软件拦截。有些安全软件会对新出现的 dll 做行为拦截导致初始化失败。把 conda 的 envs 目录整体加进白名单然后重装一遍 torch。第六条路径里有中文或特殊字符。报错里的路径C:\Users\24303\.conda\envs\pytorch是纯英文数字没问题。但如果你的用户名是中文site-packages\torch\lib的完整路径里就带中文了某些老版本 dll 的加载器处理不了。解决办法是把 conda 环境装到D:\envs\pytorch这种纯英文路径。第七条Python 是 32 位的。极少数情况装的是 32 位 Python却装 64 位 wheel装的时候 pip 会拦但有些时候不拦。python -c import struct; print(struct.calcsize(P)*8)输出 64 才对。5.2 torch.cuda.is_available() 返回 False 的排查顺序这个问题的排查要分装的时候就 False和昨天还好今天变 False两种情况。装的时候就 False按这个顺序查一torch.version.cuda是不是 None是 None 就是 CPU 版重装二nvidia-smi能不能正常输出不能就是驱动问题三nvidia-smi右上角的 CUDA 版本号是不是大于等于 11.3小于就升级驱动四显卡算力是不是在 CUDA 11.3 支持范围内。昨天还好今天变 False常见原因是驱动被 Windows Update 悄悄替换了或者你装别的软件时动了 PATH。还有一种情况是笔记本的显卡切换——插着电源用独显正常拔了电源切到核显is_available()就变 False 了。这个坑我遇到过两次第二次才反应过来。5.3 torch 与 numpy 版本打架这个报错很多人会误以为是 CUDA 的问题A module that was compiled using NumPy 1.x cannot be run in NumPy 2.0.0torch 1.10 到 1.12 这一代是在 NumPy 1.x 年代编译的如果你装环境时顺手装了个最新的 numpy 2.ximport torch 或者做张量转数组操作时就会炸。解决办法很简单pip install numpy2或者干脆锁到 1.23.5 这种经过验证的版本。顺手提一句numpy 的 ABI 兼容性问题在 Windows 上比 Linux 更容易显现因为 Windows 的 dll 加载机制更严格。5.4 高频报错速查表报错信息关键片段大概率原因处理动作WinError 1114 c10.dll缺 VC 运行时 / 双份 torch装 VC Redist重建环境Could not find a version that satisfies版本号不存在或 Python 版本不匹配核对版本矩阵换 Python 3.9No matching distribution found平台或解释器位数不符检查 64 位 Pythonmodule compiled using NumPy 1.xnumpy 2.x 冲突pip install numpy2is_available() FalseCPU 版 wheel / 驱动旧看 torch.version.cuda 判断cudnn version NonecuDNN 未随 wheel 加载检查 wheel 版本尾号是否带 cu113RuntimeError 找不到 cudart64_113.dllPATH 缺 CUDA bin补 PATH 或重装 wheel6. 装完不能只看 is_available跑通一个真正用 GPU 的数据集与训练回路6.1 手写 Dataset 与 DataLoader环境通了不代表能干活。真正跑训练的时候第一个撞上的往往是数据加载问题。我用一个不依赖任何外部数据集下载的自造数据来演示这样你在断网环境下也能复现。先定义一个最基础的数据集类这是个标准的 torch 创建数据集的写法实际项目里把读取逻辑换成读文件、读数据库就行import torch from torch.utils.data import Dataset, DataLoader class RandomImageDataset(Dataset): def __init__(self, num_samples20000, num_classes10, size(3, 32, 32)): self.num_samples num_samples self.num_classes num_classes # 一次性生成避免每个 epoch 重复构造 self.images torch.randn(num_samples, *size) self.labels torch.randint(0, num_classes, (num_samples,)) def __len__(self): return self.num_samples def __getitem__(self, index): return self.images[index], self.labels[index]这里有个经验点不要把torch.randn放在__getitem__里。我早期图省事这么干过结果 GPU 利用率一直在 20% 上下晃排查了半天以为是数据加载慢其实是每个 batch 都在 CPU 上现场生成随机数。真实项目里对应的问题就是把图片解码放在__getitem__里正确的做法是提前转成二进制格式或者用缓存。6.2 Windows 下 num_workers 的坑与 ifname保护DataLoader 的num_workers参数在 Windows 上是个雷区。Linux 用 fork 启动子进程Windows 只能用 spawn也就是重新导入一遍主模块。如果你的 DataLoader 创建代码没有放在if __name__ __main__:下面子进程导入主模块时又创建一遍 DataLoader再创建一遍子进程无限递归最后报一堆让人看不懂的错。正确的骨架长这样def build_loader(): dataset RandomImageDataset() loader DataLoader( dataset, batch_size256, shuffleTrue, num_workers4, pin_memoryTrue, persistent_workersTrue, prefetch_factor4, drop_lastTrue, ) return loader if __name__ __main__: train_loader build_loader() # 训练逻辑写在这里关于参数选择我说几个实测结论。num_workers不是越大越好Windows 上每开一个 worker 都要复制一份主进程的内存空间开销不小。在 8 核 CPU 上4 个 worker 通常是最优点开到 8 反而更慢。pin_memoryTrue只在有 GPU 的时候有意义它把数据锁在页锁定内存里H2D 拷贝会快一些。persistent_workersTrue是从 torch 1.7 开始有的可以避免每个 epoch 结束重建 worker第一个 epoch 之后能快 5% 到 10%。还有一个坑是共享内存不足。Windows 上多进程传数据靠共享内存数据量大的时候偶尔会报共享内存相关的错误这时候要么降低num_workers要么降低 batch size要么把prefetch_factor从 4 降到 2。6.3 一个能对照 CPU/GPU 的小训练脚本光看is_available()是 True 没意义得跑出速度差异才算真用上了。先做个最简单的矩阵乘法对照import torch import time def bench(device, n2048, iters10): a torch.randn(n, n, devicedevice) b torch.randn(n, n, devicedevice) # 预热第一次调用包含上下文初始化开销 for _ in range(2): _ a b if device cuda: torch.cuda.synchronize() t0 time.time() for _ in range(iters): _ a b if device cuda: torch.cuda.synchronize() return (time.time() - t0) / iters print(CPU:, bench(cpu)) print(GPU:, bench(cuda))两个要点。预热必须做第一次 CUDA 调用会初始化上下文耗时可能是稳定值的十倍以上。torch.cuda.synchronize()必须加因为 CUDA 的调用是异步的不加的话计时只统计了把任务丢进队列的时间得到的结果会离谱地快。然后是一个完整的小训练回路模型用三层卷积import torch import torch.nn as nn import time class SmallCNN(nn.Module): def __init__(self, num_classes10): super().__init__() self.features nn.Sequential( nn.Conv2d(3, 32, 3, padding1), nn.BatchNorm2d(32), nn.ReLU(inplaceTrue), nn.MaxPool2d(2), nn.Conv2d(32, 64, 3, padding1), nn.BatchNorm2d(64), nn.ReLU(inplaceTrue), nn.MaxPool2d(2), nn.Conv2d(64, 128, 3, padding1), nn.BatchNorm2d(128), nn.ReLU(inplaceTrue), nn.AdaptiveAvgPool2d(1), ) self.head nn.Linear(128, num_classes) def forward(self, x): x self.features(x) x torch.flatten(x, 1) return self.head(x) def run(): device torch.device(cuda if torch.cuda.is_available() else cpu) print(using device:, device) model SmallCNN().to(device) criterion nn.CrossEntropyLoss() optimizer torch.optim.AdamW(model.parameters(), lr1e-3) train_loader build_loader() model.train() torch.cuda.reset_peak_memory_stats() for epoch in range(2): t0 time.time() total, correct, loss_sum 0, 0, 0.0 for step, (images, labels) in enumerate(train_loader): images images.to(device, non_blockingTrue) labels labels.to(device, non_blockingTrue) optimizer.zero_grad(set_to_noneTrue) logits model(images) loss criterion(logits, labels) loss.backward() optimizer.step() loss_sum loss.item() correct (logits.argmax(1) labels).sum().item() total labels.size(0) if step % 20 0: print(fepoch {epoch} step {step} loss {loss_sum/(step1):.4f}) dt time.time() - t0 print(fepoch {epoch} acc {correct/total:.4f} time {dt:.2f}s) print(peak mem MB:, torch.cuda.max_memory_allocated() / 1024 / 1024) if __name__ __main__: run()non_blockingTrue配合pin_memoryTrue才有意义两者缺一不可。set_to_noneTrue比默认的zero_grad()稍快因为它直接把梯度置为 None 而不是填充零torch 1.7 之后都支持。6.4 训练过程中的显存与吞吐观察跑起来之后另开一个命令行窗口跑nvidia-smi -l 1每秒刷新一次。你要观察三个数显存占用、GPU 利用率、以及功耗。显存占用稳定在某个值不涨说明没有泄漏如果每个 epoch 涨一点八成是把张量存进了 list 里忘了 detach梯度图一直挂在计算图上。GPU 利用率如果在 30% 到 60% 之间波动说明数据加载跟不上考虑把num_workers调大或者把数据预处理挪到 GPU 上做如果稳定在 90% 以上说明这个配置基本吃满显卡了。还有一个非常容易被忽略的点torch.cuda.max_memory_allocated()报的是 PyTorch 分配器记录的峰值而nvidia-smi显示的是整个进程的显存占用包括上下文、cuDNN workspace 等。两者差个几百兆是正常的差得特别多的时候可能是 cuDNN 在自动调优benchmark 模式占用了额外 workspace可以试试torch.backends.cudnn.benchmark False对比一下。7. 踩坑之后我固定下来的几条习惯写到这里过程基本讲完了最后分享几条我自己在反复重装里固化下来的做法都是踩过坑才改的习惯。第一条环境名带版本号。我不再用pytorch这种名字改成torch111_cu113_py39看一眼环境名就知道里面是什么组合不至于半年后面对五六个环境发呆。同时每个环境目录下留一个requirements-lock.txt用pip freeze生成重装的时候直接照着装。第二条先写验证脚本再装东西。我在每个新环境里都会放一个check_env.py内容就是第 4.3 节那几行加上矩阵乘法基准测试装完立刻跑一遍把输出贴到项目 README 里。这样过半年回来看能确认当时的环境确实是通的。第三条不在同一个环境里混 pip 和 conda。要换安装方式就删环境重建这是成本最低的方案。混装带来的问题排查成本远高于重建环境的十分钟。第四条重装前先备份 wheel 缓存。pip 的缓存目录默认在%LOCALAPPDATA%\pip\Cache那几个 G 的 cu113 wheel 下载一次就存着重装环境时可以直接复用。我通常还会额外把那几个 whl 文件拷到一个单独的盘避免缓存被清理工具顺手删掉。第五条遇到 WinError 1114 这类 dll 问题先装 VC Redist 再动手。我现在遇到这个报错的第一反应已经不是去搜了直接装一遍运行时库八成直接好。这个动作应该排在任何深度排查之前因为它最便宜。第六条记录驱动版本。重装系统或者更新驱动之后环境失效往往就是驱动版本变了。我现在会把nvidia-smi的完整输出存一份到环境目录里包括驱动号和支持的 CUDA 上限出问题的时候一对比就知道是不是驱动被动过。这几条看起来都是小事但每一条背后都对应着我浪费掉的几个小时。CUDA 这套东西踩坑不可怕可怕的是同一个坑踩三次还找不到原因。
返回列表