ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

PyTorch CUDA不可用?从False到True的完整排查指南

PyTorch CUDA不可用?从False到True的完整排查指南

1. 从一次深夜调试说起:那个让人抓狂的False

如果你写过PyTorch代码,大概率在某个时刻敲下过这行命令:

import torch print(torch.cuda.is_available())

然后满怀期待地等着屏幕上跳出那个令人安心的True,结果等来的却是一个冷冰冰的False。更让人崩溃的是,你明明记得昨天还是True,今天重启了一下机器就变了;或者你刚换了一台新机器,显卡看起来一切正常,驱动也装了,但PyTorch就是不认。

这个问题的排查难度在于,它不像语法错误那样会给你一个明确的报错信息。False只是一个结果,它背后可能对应着七八种完全不同的原因。你如果不知道排查的顺序,很容易陷入"重装驱动→重装CUDA→重装PyTorch→还是False→再重装"的死循环。

我自己在这个问题上前后踩过至少五次坑,每一次的原因都不一样。有一次是驱动版本和CUDA运行时不匹配,有一次是装成了CPU版本的PyTorch,还有一次最离谱——系统里有两套Python环境,我在终端里激活的和Jupyter Notebook里用的根本不是同一个。

这篇文章就是把这些年积累的排查经验完整地梳理一遍。我会按照"从最表层到最底层"的顺序,把每一个可能导致False的原因拆开来讲清楚,包括怎么判断是不是这个问题、怎么修复、以及修复之后怎么验证。不管你是刚接触深度学习的新手,还是已经跑过几个项目的老手,这篇内容都值得收藏,因为这个问题你迟早还会再遇到。

2. 先搞清楚is_available()到底在检查什么

2.1 这个函数的判断逻辑不是"有没有显卡"

很多人对torch.cuda.is_available()有一个误解,以为它是在检测"这台机器上有没有NVIDIA显卡"。实际上不是的。这个函数的判断逻辑要复杂得多,它本质上是在回答一个问题:PyTorch当前这个进程,能不能正常使用CUDA进行计算?

具体来说,它内部会依次检查以下几个条件:

  • PyTorch这个包本身是不是CUDA版本(而不是CPU-only版本)
  • 系统里能不能加载到CUDA运行时库(比如libcudart.so或cudart64_*.dll)
  • 显卡驱动是否正常加载,驱动版本是否满足CUDA运行时要求的最低版本
  • 能不能成功创建一个CUDA上下文(这一步会实际调用驱动接口)

只要上面任何一环出了问题,返回值就是False。所以你不能简单地把它等同于"有没有显卡"。

2.2 为什么它不告诉你具体哪里出了问题

这是PyTorch设计上一个让人又爱又恨的地方。is_available()只返回一个布尔值,不提供任何诊断信息。它的定位就是一个快速检查的开关,不是诊断工具。

但PyTorch其实提供了更详细的诊断手段。你可以用torch.version.cuda查看PyTorch编译时链接的CUDA版本,用torch.backends.cudnn.version()查看cuDNN版本,通过nvidia-smi查看驱动支持的CUDA版本。这几个信息组合起来,基本就能定位问题所在。

记住一个原则:is_available()返回False的时候,不要急着重装任何东西。先收集信息,再动手。

2.3 一个容易被忽略的事实:编译版本和运行时版本是两回事

PyTorch在发布的时候,会针对不同的CUDA版本编译出不同的wheel包。比如torch-2.x.x+cu118表示这个包是用CUDA 11.8编译的,torch-2.x.x+cu121表示用CUDA 12.1编译的。你通过pip安装的时候,默认可能会装到CPU版本,也可能装到某个CUDA版本,取决于你的pip源和安装命令。

而系统里实际安装的显卡驱动,支持的最高CUDA版本是另一个概念。驱动版本决定了你"最高能用哪个版本的CUDA运行时",而PyTorch编译时链接的CUDA版本决定了它"需要哪个版本的运行时"。

这两个版本如果不匹配,就会出现各种问题。最常见的情况是:PyTorch编译时用的是CUDA 12.1,但系统驱动太老,只支持到CUDA 11.8,这时候is_available()就会返回False。

3. 排查第一步:确认你装的是不是CPU版本的PyTorch

3.1 这是最常见也最容易修复的原因

我敢说,初次遇到False的人里面,至少有三分之一是因为装成了CPU版本的PyTorch。尤其是用pip install torch这种不带任何后缀的命令安装的时候,pip默认会从PyPI拉取CPU版本的包。

怎么确认?运行下面这行代码:

import torch print(torch.__version__)

如果输出类似2.1.0+cpu这样的字符串,那基本可以确定你装的是CPU版本。注意那个+cpu后缀,这是最明确的标志。

如果输出的是2.1.0后面没有任何后缀,那可能是CUDA版本,也可能是CPU版本,需要进一步确认。你可以用:

print(torch.version.cuda)

如果输出是None,那就是CPU版本无疑了。如果输出类似11.8或12.1,说明是CUDA版本。

3.2 正确的安装命令长什么样

确认是CPU版本之后,卸载重装就行。但安装命令有讲究,不能随便写。以CUDA 11.8为例,正确的命令是:

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

注意--index-url这个参数,它指定了从PyTorch官方的CUDA专用源下载,而不是从默认的PyPI。如果你不加这个参数,pip还是会给你装CPU版本。

对于CUDA 12.1,把cu118换成cu121即可。安装完成后再运行一次torch.version.cuda,应该能看到对应的版本号。

3.3 一个真实的踩坑案例

我之前帮一个同学排查这个问题,他的情况特别典型。他在终端里用pip install torch装了PyTorch,然后在Jupyter Notebook里测试,一直是False。我让他运行torch.__version__,输出是2.0.1+cpu,确认是CPU版本。

然后他卸载重装,用了正确的CUDA源,终端里测试通过了,True。但Jupyter Notebook里还是False。这就引出了下一个大坑——多环境问题。

4. 多Python环境下的"薛定谔的CUDA"

4.1 为什么终端里是True,Notebook里是False

这个问题的根源在于:你的终端和你的Jupyter Notebook可能使用的是两个完全不同的Python解释器。

在终端里,你运行python命令,调用的可能是/usr/bin/python3或者某个conda环境里的Python。而Jupyter Notebook启动的时候,它绑定的是某个特定的kernel,这个kernel背后是另一个Python解释器。

如果你在终端里给A环境装了CUDA版本的PyTorch,但Jupyter用的是B环境的kernel,那B环境里可能还是CPU版本,或者根本没装PyTorch。

4.2 怎么确认当前用的是哪个Python

在终端里:

which python python -c "import sys; print(sys.executable)"

在Jupyter Notebook里:

import sys print(sys.executable)

对比这两个输出。如果路径不一样,那就是环境不一致的问题。

4.3 统一环境的几种做法

最直接的做法是在Jupyter里安装一个指向正确环境的kernel:

# 假设你的正确环境叫myenv conda activate myenv pip install ipykernel python -m ipykernel install --user --name=myenv --display-name="Python (myenv)"

然后在Jupyter的Kernel菜单里选择"Python (myenv)"。这样Notebook和终端就用的是同一个环境了。

另一种做法是直接在Jupyter的cell里用!pip install来安装,这样装到的就是当前kernel对应的环境。但我不太推荐这种做法,因为!pip有时候会指向错误的pip,反而不如显式指定环境来得可靠。

经验之谈:但凡是"终端里正常、Notebook里不正常"或者反过来"Notebook里正常、终端里不正常"的情况,九成以上都是环境不一致导致的。先查sys.executable,能省下大量重装的时间。

5. 驱动、CUDA运行时、PyTorch三者的版本匹配问题

5.1 用nvidia-smi读出关键信息

在终端里运行:

nvidia-smi

这个命令会输出一个表格,右上角有一个"CUDA Version: XX.X"的标识。注意,这个数字不是你系统里安装的CUDA版本,而是你的显卡驱动最高支持的CUDA运行时版本。

举个例子,如果显示"CUDA Version: 12.2",意味着你的驱动可以支持最高12.2版本的CUDA运行时。你可以安装11.8的CUDA运行时,也可以安装12.1的,都没问题。但如果你安装12.3的CUDA运行时,就会因为驱动太老而无法使用。

5.2 版本匹配的规则

这里有一个简化的匹配规则表:

驱动显示的CUDA Version可以使用的PyTorch CUDA版本典型安装命令后缀
11.811.8及以下cu118
12.112.1及以下cu121
12.212.2及以下cu121(12.2通常兼容)
12.412.4及以下cu124

关键原则是:PyTorch编译时用的CUDA版本,不能高于驱动支持的最高版本。

如果你不确定,最保险的做法是选择一个比驱动支持版本低一到两个小版本的PyTorch。比如驱动显示12.4,你装cu121的PyTorch,基本不会有问题。

5.3 驱动太老怎么办

如果nvidia-smi显示的CUDA Version太低,比如只有11.4,而你想用cu118的PyTorch,那就需要更新显卡驱动。

在Linux上,可以通过包管理器更新,也可以从NVIDIA官网下载runfile手动安装。手动安装的时候注意要先把旧驱动卸干净,否则容易出现版本冲突。

在Windows上,直接下载最新的驱动安装包,运行安装程序,选择"自定义安装"并勾选"执行清洁安装",这样会先卸载旧驱动再装新的。

更新完驱动之后重启系统,再运行nvidia-smi确认版本已经上去了。

5.4 一个隐蔽的坑:CUDA运行时被LD_LIBRARY_PATH干扰

在Linux系统上,有时候系统里装了多个版本的CUDA运行时,而LD_LIBRARY_PATH环境变量指向了一个错误的版本。这种情况下,即使PyTorch本身没问题,加载的时候也会加载到错误的库,导致is_available()返回False。

排查方法:

echo $LD_LIBRARY_PATH

看看输出的路径里有没有包含CUDA相关的目录。如果有,确认一下这些目录下的CUDA版本是否和PyTorch匹配。如果不匹配,可以临时清空这个变量测试:

LD_LIBRARY_PATH="" python -c "import torch; print(torch.cuda.is_available())"

如果这样能返回True,那就说明是LD_LIBRARY_PATH的问题,需要调整环境变量配置。

6. 那些不常见但确实存在的奇葩原因

6.1 显卡被其他进程独占

在某些情况下,显卡可能被其他进程独占了。比如你之前跑了一个训练任务没有正常退出,显存没有释放,或者在某些配置下显卡被设置成了独占模式。

用nvidia-smi可以看到当前显卡上有哪些进程在运行。如果显存占用很高但你没有在跑任何任务,那可能就是有僵尸进程。找到对应的PID,用kill -9结束掉。

6.2 Docker容器里没有透传显卡

如果你是在Docker容器里运行PyTorch,那需要确保容器启动的时候做了显卡透传。否则容器里根本看不到显卡,is_available()自然是False。

启动容器的时候需要加上--gpus all参数(需要先安装nvidia-container-toolkit)。进入容器后运行nvidia-smi,如果能正常输出,说明透传成功了。

6.3 WSL2环境下的特殊配置

在WSL2里使用显卡,需要Windows端的驱动支持WSL。如果Windows端的驱动太老,或者没有安装WSL专用的驱动组件,WSL里就看不到显卡。

确认方法是在WSL里运行nvidia-smi,如果提示命令找不到或者报错,那就是WSL的显卡支持没配好。需要去NVIDIA官网下载支持WSL的Windows驱动,安装后重启WSL。

6.4 硬件本身的问题

虽然概率很低,但确实遇到过显卡硬件故障导致is_available()返回False的情况。这种时候nvidia-smi可能也会报错,或者干脆没有任何输出。

如果排除了所有软件层面的原因,可以尝试把显卡插到另一台机器上测试,或者用其他工具(如CUDA Samples里的deviceQuery)来确认显卡是否正常工作。

7. 一套完整的排查流程

7.1 从外到内的检查顺序

我把上面所有的排查点整理成一个有序的流程,你按照这个顺序走一遍,基本能覆盖95%以上的情况:

  1. 运行nvidia-smi,确认显卡能被系统识别,驱动正常加载
  2. 运行python -c "import torch; print(torch.__version__)",确认PyTorch版本
  3. 运行python -c "import torch; print(torch.version.cuda)",确认是否为CPU版本
  4. 如果是CPU版本,用正确的CUDA源重装
  5. 确认终端和IDE/Notebook使用的是同一个Python环境
  6. 检查驱动支持的CUDA版本是否满足PyTorch的要求
  7. 检查LD_LIBRARY_PATH是否指向了错误的CUDA库
  8. 如果是容器环境,确认显卡透传配置
  9. 如果是WSL2,确认Windows端驱动支持

7.2 一个万能的诊断脚本

我把常用的诊断信息写成了一个脚本,遇到问题的时候直接跑一遍,所有关键信息一目了然:

import torch import sys import os print("=" * 50) print(f"Python executable: {sys.executable}") print(f"Python version: {sys.version}") print(f"PyTorch version: {torch.__version__}") print(f"CUDA available: {torch.cuda.is_available()}") print(f"PyTorch CUDA version: {torch.version.cuda}") print(f"cuDNN version: {torch.backends.cudnn.version()}") print(f"LD_LIBRARY_PATH: {os.environ.get('LD_LIBRARY_PATH', 'Not set')}") print(f"CUDA_HOME: {os.environ.get('CUDA_HOME', 'Not set')}") if torch.cuda.is_available(): print(f"Device count: {torch.cuda.device_count()}") print(f"Device name: {torch.cuda.get_device_name(0)}") print(f"Capability: {torch.cuda.get_device_capability(0)}") print("=" * 50)

把这个脚本保存成check_cuda.py,在终端里运行python check_cuda.py,把输出结果保存下来。不管你是自己排查还是去社区提问,这份信息都是最有价值的。

7.3 排查时容易犯的几个错误

第一个错误是"一上来就重装"。重装驱动、重装CUDA、重装PyTorch,一套下来少说半小时,而且很可能根本没解决问题,因为方向就错了。

第二个错误是"只看一个信息就下结论"。比如看到nvidia-smi正常就认为驱动没问题,但实际上驱动正常不代表版本匹配。

第三个错误是"忽略环境问题"。尤其是在Windows上,很多人同时装了多个Python版本,加上conda环境,情况非常复杂。不确认清楚当前用的是哪个Python,后面的排查都是白费功夫。

8. 修复之后的验证与长期维护建议

8.1 验证不只看is_available()

is_available()返回True只是第一步。真正要确认CUDA能正常工作,还需要做一次实际的张量运算:

import torch # 基础检查 assert torch.cuda.is_available(), "CUDA not available" # 实际运算测试 x = torch.randn(1000, 1000).cuda() y = torch.randn(1000, 1000).cuda() z = torch.mm(x, y) print(f"Matrix multiplication result shape: {z.shape}") print(f"Device: {z.device}") # 检查显存分配 print(f"Allocated: {torch.cuda.memory_allocated() / 1024**2:.2f} MB") print(f"Cached: {torch.cuda.memory_reserved() / 1024**2:.2f} MB")

这段代码会实际在显卡上分配内存并做一次矩阵乘法。如果这一步能跑通,说明CUDA环境是真正可用的,而不只是is_available()返回了True。

8.2 用conda环境锁定版本

为了避免以后再次出现版本不匹配的问题,我强烈建议用conda来管理环境,并且在创建环境的时候就锁定版本:

conda create -n pytorch_env python=3.10 conda activate pytorch_env conda install pytorch torchvision torchaudio pytorch-cuda=11.8 -c pytorch -c nvidia

用conda安装的好处是,它会自动处理CUDA运行时和PyTorch之间的依赖关系,减少手动匹配版本的工作量。

8.3 记录环境配置

每次配好一个能正常工作的环境之后,把关键信息记录下来:

pip freeze > requirements.txt nvidia-smi > gpu_info.txt

这样下次换机器或者重装系统的时候,直接照着记录来配,不用再从头排查一遍。

8.4 关于版本升级的策略

我的建议是:如果一个环境能正常工作,不要轻易升级。尤其是显卡驱动和CUDA运行时,升级带来的收益往往不值得冒的风险。除非你需要用到某个新版本才支持的特性,否则保持现状是最稳妥的选择。

如果确实需要升级,先在另一个环境里测试通过之后再切换,不要直接在主环境上操作。

9. 写在最后的一些个人体会

排查torch.cuda.is_available()返回False这个问题,最核心的能力不是技术知识,而是排查的思路和顺序。你知道先看什么、再看什么,就能在几分钟内定位问题。如果顺序反了,可能折腾半天还在原地打转。

我自己的习惯是:遇到这个问题,第一件事永远是跑那个诊断脚本,把sys.executable、torch.__version__、torch.version.cuda、nvidia-smi这四个信息拿到手。这四个信息一出来,问题基本就定位了八成。

另外说一个心态上的事情。这个问题有时候确实会让人很烦躁,尤其是赶项目的时候突然发现显卡用不了了。但越是这种时候越要冷静,按照流程一步步来。我见过太多人因为着急,把本来只是环境变量的问题搞成了重装系统,反而浪费了更多时间。

还有一个实用的小技巧:如果你用的是远程服务器,而且有多张显卡,记得检查一下CUDA_VISIBLE_DEVICES这个环境变量。有时候这个变量被设置成了空字符串或者一个不存在的设备编号,也会导致is_available()返回False。这个坑很隐蔽,因为nvidia-smi看起来一切正常,但PyTorch就是看不到显卡。

echo $CUDA_VISIBLE_DEVICES

如果输出是空或者不是你期望的值,用export CUDA_VISIBLE_DEVICES=0(或者对应的编号)来修正。

最后,如果你按照这篇文章的流程走了一遍还是没解决,那大概率是硬件层面的问题了。这时候可以尝试用CUDA官方提供的deviceQuery工具来做一个最终的确认。如果deviceQuery也报错,那就不是PyTorch的问题了,需要从显卡硬件或者驱动层面去排查。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进