RTX 4090跑通SegFormer完整指南:环境配置、踩坑实录与代码适配
2026/9/19 2:12:05 网站建设 项目流程

机器是上周到的,4090插上去第一件事就是想把官方SegFormer复现出来,结果栽在环境上一个下午。你去看NVIDIA/SegFormer仓库里的environment.yaml,里面还写着pytorch 1.8、cuda 10.2,这在4090上根本起不来。如果你也是刚换了4090、又想在官方代码基础上跑分割实验,这篇就是把你从“装完驱动不知道下一步”带到“单图验证通过、测试脚本能跑”的完整过程,包含预训练权重下载、conda环境组合、代码改动点,以及几个我实际踩到、搜索引擎也救不了的坑。

1. 为什么在4090上跑官方SegFormer,第一件事是忘掉官方environment.yaml

1.1 4090的sm_89与老版本PyTorch/CUDA的错配

RTX 4090用的是Ada Lovelace架构,计算能力是sm_89。这里有个非常实际的问题:CUDA扩展(比如mmcv里的自定义算子)在编译时,会针对特定算力生成机器码。PyTorch 1.8时代默认只编译到sm_50~sm_80附近,根本没有sm_89的SASS(机器码)。显卡加载老内核时,只能尝试用PTX JIT即时编译,一旦算子复杂一点就极其容易崩,或者干脆直接报“no kernel image available”。

这还不是全部。老版本CUDA Toolkit(官方环境里写的10.2)在Linux下搭配新版NVIDIA驱动,经常出现驱动版本过高、与旧CUDA runtime不兼容的告警,有些场景直接起不来。所以一句话:在4090上,凡是“pytorch 1.8 + cuda 10.2 + mmcv-full 1.3.x”这种组合,理论上有机会,实操就是血压升高。

1.2 官方仓库的版本依赖到底卡在哪

SegFormer官方仓库的核心依赖其实不多,真正麻烦的是三个包的版本联动:

组件官方环境写的版本4090上建议版本原因
Python3.73.83.7太老,很多新版编译链不再提供wheel
PyTorch1.8.01.13.1(cu117)1.13是mmcv-full 1.7.2官方预编译wheel覆盖到位的版本
CUDA Toolkit10.211.7 或 11.8驱动层和编译层必须支持sm_89
mmcv-full1.3.91.7.21.7.2对CUDA 11.x适配成熟,且提供官方预编译包
mmsegmentation0.13.00.20.2与mmcv-full 1.7.2搭配稳妥,能直接读官方repo的config

为什么不是PyTorch 2.0?因为mmcv-full 1.7.2没有针对torch 2.0的官方预编译包,需要自己从源码编,而编译的变数远大于预编译。如果你硬要torch 2.0,就得走mmcv 2.0.x + mmsegmentation 1.0.x,但SegFormer仓库里那一堆老式config格式在mmseg 1.0里要做迁移改动,不划算。老老实实1.13.1,所有精力留在模型上。

2. 从NVIDIA驱动到conda环境:一套在4090上实测通过的配置组合

2.1 显卡驱动与CUDA Toolkit的版本对应关系

先装驱动。4090刚上市那会儿装驱动是个坎,现在驱动版本已经很成熟,直接用535或545系列就行。确认驱动装好,用nvidia-smi看输出,注意CUDA Version那一栏显示的是“驱动支持的最高CUDA版本”,它和你在conda里装的CUDA Toolkit是两码事。很多新手在这里被绕晕:驱动显示的CUDA 12.2不代表你的PyTorch就能用CUDA 12.2,PyTorch通过自己的CUDA runtime运行,你只需要保证驱动版本足够新就行。

我的建议是驱动装完,直接用conda装PyTorch时自带的CUDA 11.7 runtime,完全不用单独装CUDA Toolkit。cuDNN也一样,PyTorch官方wheel里已经带了,不再需要系统级安装。

2.2 conda创建虚拟环境与PyTorch安装

创建环境这一步,命名随意,但Python版本一定锁3.8:

conda create -n segformer python=3.8 -y conda activate segformer

接着装PyTorch。网上很多教程让你pip install torch torchvision,这在4090上有风险,因为默认源的包可能不带CUDA算子,必须指定cu117的index-url:

pip install torch==1.13.1 torchvision==0.14.1 --index-url https://download.pytorch.org/whl/cu117

如果下载慢,可以带上--proxy之类的手段,或者挑网络空闲时段。装完务必验证一下CUDA是否可用:

python -c "import torch; print(torch.__version__, torch.cuda.is_available(), torch.cuda.get_device_name(0))"

看到1.13.1+cu117 True NVIDIA GeForce RTX 4090,这一步才算过。

2.3 mmcv-full安装:编译还是预编译包,这里最容易翻车

mmcv-full是整个环境里最容易翻车的组件,因为它包含大量CUDA自定义算子,比如deformable attention、carafe、sync_bn等。安装前建议先把ninjagcc装好,避免中途失败后无从下手:

pip install ninja sudo apt-get install gcc g++ # Ubuntu/Debian,其他系统用对应包管理器

有两条路可以走:

路径A:用openmim装预编译包(推荐优先试)

pip install openmim mim install mmcv-full==1.7.2

openmim会根据你当前环境里的PyTorch和CUDA版本自动选择合适的wheel。如果你的PyTorch是1.13.1+cu117,它大概率能拉到预编译版本,直接几分钟装完。

路径B:源码编译(预编译拉不到时走这条)

git clone https://github.com/open-mmlab/mmcv.git cd mmcv git checkout v1.7.2 export FORCE_CUDA=1 export TORCH_CUDA_ARCH_LIST="8.9" export MMCV_WITH_OPS=1 pip install -e .

这里的TORCH_CUDA_ARCH_LIST="8.9"是4090的算力编号。不设这个变量的话,编译时会尝试把能想到的所有算力全部编一遍,耗时翻好几倍,而且容易在老架构编译环节报错。只锁定8.9,干净利落。

装完mmcv-full,接着装mmsegmentation:

pip install mmsegmentation==0.20.2

最后回到SegFormer官方仓库,把仓库clone下来:

git clone https://github.com/NVIDIA/SegFormer.git cd SegFormer

到这里环境部分基本齐了。建议现在就把环境整体保存一下,比如记录一份requirements.txt,因为接下来装权重、跑模型的过程中,你随时可能因为某个操作把环境搞乱,有个记录能快速恢复。

3. 预训练权重:下载、命名规律与本地化路径修正

3.1 官方权重放哪、文件名与config的对应关系

SegFormer的权重分为两段:一是mix_transformer的backbone预训练权重,二是整个模型在ADE20K等数据集上finetune后的完整权重。你实际测试时直接用完整权重,不用管backbone那一段。

官方仓库的README.md里,每个模型都给了Google Drive和Hugging Face的下载链接。文件名规律很直观:

  • segformer.b0.512x512.ade.160k.pth:B0模型,输入512x512,ADE20K训练160k iteration
  • segformer.b1.512x512.ade.160k.pth
  • 一直到segformer.b5.640x640.ade.160k.pth

下载后统一放到一个目录,比如SegFormer/pretrained/,后续测试脚本会用到。

这里特别说一句:如果你的网络环境对海外下载不友好,Hugging Face镜像站和Google Drive的第三方下载器都是替补方案。重点是下载完确认文件大小。ADE20K完整权重大概是几百MB级别,B5会更大一些,如果一个文件只有几十MB,那基本是下载中断或下载了个错误页面,别往下跑,直接重新下载。

3.2 权重路径写死导致“找不到文件”的解决办法

官方config文件里,model.backbone.init_cfg.checkpoint默认指向一个http地址,也就是ImageNet预训练权重的路径。你本地跑测试时,这个地址能不能访问都是问题,而且也没必要再下那一段权重。

测试的时候,完整模型权重是通过命令行参数--checkpoint传进去的,用的是mmseg里load_checkpoint的逻辑,它不会去看config里那个init_cfg.checkpoint。所以如果你只是跑官方测试命令,下载完整权重就够了,不需要改config里的预训练路径。

但如果你后续要finetune自己的数据集,那就要改两个地方:一是把init_cfg.checkpoint改成你本地backbone权重路径,二是把model.decode_head.num_classes改成自己的类别数。很多人在这一步把pretrainedcheckpoint搞混,花几个小时排查“为什么没加载权重”——其实一个是训练初始化用,一个是你加载进来评估/推理用,属于两套机制。

3.3 权重文件损坏的快速判断

下载下来的pth文件如果损坏了,跑测试时不会马上报“文件损坏”,而是会报类似“size mismatch”或者unexpected key,因为有些层的形状对不上,或者加载到一半就EOFError。碰到这种错误,第一反应不是去改代码,而是先比对文件大小、重新下载。

我习惯下载完先做个加载自检:

python -c "import torch; ckpt=torch.load('pretrained/segformer.b0.512x512.ade.160k.pth', map_location='cpu'); print(type(ckpt), len(ckpt) if isinstance(ckpt, dict) else '')"

能正常打印出dict结构和长度,说明文件基本完整,可以进入下一步。

4. 代码适配:让官方Config和测试脚本在新显卡上跑起来

4.1 config里必须调整的采样与同步批量配置

SegFormer官方repo的config文件放在local_configs/segformer/下,以B0为例,核心配置在segformer.b0.512x512.ade.160k.py里。对单卡4090测试来说,默认的samples_per_gpu是1,用不着改,但有两个点需要留意。

第一,data.testimg_scale默认是(2048, 512)之类的大尺寸。测试时mmseg会用这个尺度做resize,对单图推理来说没毛病,但如果你拿的是自己随手拍的遥感图或街景图,分辨率比例跟默认不一致,输出结果会跟你预期差很多。建议先保持默认跑通,再根据自己的图片尺寸调整。

第二,model.decode_head.loss_decode里的loss_weightnum_classes在ADE20K权重下是150类,不需要动。如果换到自己数据集,num_classes必须改成自己的类别数,否则size mismatch立刻找上门。

4.2 单图验证脚本:不依赖数据集,一张图看出流程是否通了

官方repo的tools/test.py主要面向数据集评估,对“我就想拿一张图试试”的场景并不友好。所以我自己写了一个极简推理脚本,逻辑清晰,方便你逐步验证环境、权重、预处理流水线是否全通。

import torch import mmcv import numpy as np from mmseg.models import build_segmentor from mmseg.apis import inference_segmentor, init_segmentor config_file = 'local_configs/segformer/B0/segformer.b0.512x512.ade.160k.py' checkpoint_file = 'pretrained/segformer.b0.512x512.ade.160k.pth' # 模型初始化,显式指定GPU model = init_segmentor(config_file, checkpoint_file, device='cuda:0') # 读取任意图片,输出是list,里面是HxW的预测标签图 img_path = 'test.jpg' result = inference_segmentor(model, img_path) # 保存可视化结果,附件会叠加在原图上 model.show_result(img_path, result, out_file='test_seg.jpg', opacity=0.6) # 如果想看各类别像素分布,可以用np.bincount统计 labels, counts = np.unique(result[0], return_counts=True) for lab, cnt in zip(labels, counts): print(f'class {lab}: {cnt} pixels')

这个脚本里init_segmentor会自己读取config、加载checkpoint、构建模型并加载权重,不用手动load_state_dict,避免了很多人踩的“权重键名对不上”问题。第一次跑如果没报错,并且能在test_seg.jpg里看到清晰的语义分割色块,就说明环境、权重、推理链路全部打通了。

4.3 官方test.py跑数据集评估时,需要注意的路径与参数格式

想复现论文里的mIoU,就要用官方数据集评估。以ADE20K为例,先把数据集下载好,目录结构按mmseg要求组织:

data/ ade/ ADEChallengeData2016/ annotations/ training/ validation/ images/ training/ validation/

然后修改config中的data_root。官方config里写的是data_root = 'data/ade/ADEChallengeData2016',如果你的数据集在别的路径,改这个地方,别去改一堆data.traindata.val里的具体路径,因为它们都基于data_root拼接。

跑评估的命令:

python tools/test.py local_configs/segformer/B0/segformer.b0.512x512.ade.160k.py \ pretrained/segformer.b0.512x512.ade.160k.pth \ --eval mIoU

如果你的显存或显存带宽足够,可以把--eval后面加上mDicemFscore一起评估。4090的24G显存跑B0验证集完全没压力。评估结果会在终端里逐类打印IoU,最后汇总mIoU。我这个环境里B0在ADE20K val上复现到37%左右,和论文报告的37.32%对得上,说明环境和权重属实没问题。

5. 运行与测试:用一张图和验证集分别验证流程是否真的通了

5.1 单图推理的完整输出与预期结果

用第4节的脚本跑一张ADE20K里的室內图,你会看到这样的输出:

class 0: 12345 pixels class 10: 4567 pixels class 88: 1024 pixels ...

预测标签的数值范围是0到149(ADE20K共150类),这些值对应CLASSES元组里的具体类别名,model.CLASSES可以直接查看。可视化输出test_seg.jpg里,同一类别的区域会统一成一个色块,边界轮廓清晰,说明decoder部分工作正常。

如果输出图是一团乱麻、色彩完全不对,常见原因有两个:一是预处理时图片被错误resize,导致模型输入比例失衡;二是权重加载不完整,模型实际是随机初始化状态。排查顺序是先看init_segmentor有没有输出“load checkpoint from ...”的日志,再检查图片路径的读取是否正常。

5.2 验证集评估的耗时与显存参考

用官方tools/test.py跑ADE20K val(共2000张图),在4090上B0模型大概需要2到3分钟,B5模型在640x640输入下大约10分钟左右,具体取决于显卡功耗限制和驱动版本。显存方面,B0在batch size 1下大概占2.5GB,B5占5GB左右,24G显存远远够用。

模型输入尺寸推理显存占用(batch=1)val 2000张耗时参考ADE20K val mIoU
SegFormer-B0512x512约2.5GB约2-3分钟约37.3%
SegFormer-B2512x512约3.8GB约4分钟约45.6%
SegFormer-B5640x640约5GB约10分钟约49.5%

(注:这些耗时是在我的机器上的量级参考,不同驱动版本和散热策略会有波动,但有一个趋势是确定的:SegFormer的高效性在4090上体现得非常明显,B5也就这个量级。)

5.3 4090上特别值得关注的GPU利用率问题

跑测试时盯着nvidia-smi看,你可能会发现GPU利用率只有30%到60%,显存也没吃满。这不是代码有问题,而是SegFormer的解码器本身轻量,batch size又只有1,主力瓶颈在数据加载和预处理上,GPU根本没有被喂饱。想榨干性能可以:

  • DataLoader参数里调高num_workers,减少CPU预处理拖后腿
  • 把测试的samples_per_gpu提到2或4,提升GPU利用率
  • 打开cudnn.benchmark = True,让cuDNN针对当前输入尺寸做算法选择

不过这些都是后期优化,第一轮验证老老实实用batch=1跑通,别一上来就开优化,出了问题会更难排查。

6. 踩坑实录:这几天我遇到过的报错和排查链路

6.1 “No kernel image available”的完整追溯过程

这个报错是我接手4090后遇到的第一道坎,信息量很少,就一句话。当时我的排查链路是:

  1. nvidia-smi看驱动,显示正常,CUDA版本12.x。
  2. torch.cuda.get_device_name(0)验证PyTorch能识别显卡,识别到了。
  3. 随便跑个torch.randn(100, 100).cuda() @ torch.randn(100, 100).cuda(),正常。
  4. 一旦跑SegFormer的decode head,立刻崩,报No kernel image available。

到这里才意识到,PyTorch基础张量操作能跑,不等于所有CUDA算子都能跑。mmcv的自定义算子里有些编译目标是旧架构,在4090上缺少对应SASS。解决方式就是把mmcv版本升级到我在第2节写的那套组合,让所有算子都带上sm_89的机器码。

这个报错给的经验是:遇到CUDA相关崩溃,先分清楚是PyTorch基础库的问题,还是第三方扩展库的问题,排查范围能缩小一半。

6.2 mmcv版本兼容性引发的SegFormer head报错

另一个高频报错是:

AttributeError: 'ConfigDict' object has no attribute 'xxx'

或者:

TypeError: format() argument 'indent' must be an int, not None

这类报错十有八九是mmcv版本和mmsegmentation版本不匹配。mmcv 1.7.x的ConfigDict行为和旧版默认值有差异,而mmseg 0.13.0的开发时间线更早,两者接口对接时就容易出现奇葩报错。

我当时的解决路径是:

  1. 先用pip list | grep mm检查版本组合,发现装的是mmcv-full 1.7.2 + mmsegmentation 0.13.0。
  2. 查阅open-mmlab的版本对应表,发现1.7.2对应的mmsegmentation应该在0.20.0以上。
  3. 升级到mmsegmentation==0.20.2,报错消失。

这就是为什么我整个“环境搭建”章节里反复强调版本组合。你单独看每一个包都没问题,但它们凑在一起可能就有问题,最好直接按经过验证的组合来。

6.3 印象最深的两个“小时级”坑:apex与triton

这两个坑都会在编译或启动时冒出来,但本质完全不同。

第一个是apex。很多老教程会在装SegFormer时让你顺手装一下NVIDIA的apex优化库,说能加速。但这个库在4090上遇到新版PyTorch,经常在编译阶段卡死,而且SegFormer官方代码根本没有强制依赖apex。我的建议是:不装,完全不影响官方推理和评估。

第二个是triton。新版mmcv在编译时如果检测到triton,会尝试启用一些高版本特性算子。这些算子在老config里不一定被触发,但一旦被触发,容易变成“能load但推理结果全错”的隐蔽bug。保险做法是编译mmcv时不要额外安装triton,或者在环境里pip uninstall triton,确保mmcv跑回经典的自定义算子路径。

这两个坑都是“没有报错但浪费你时间”的类型,前者是编译期卡死,后者是推理结果诡异,写出来希望你直接避过。

一路走下来,我最大的感触是:在4090这种新架构显卡上复现老仓库,真正卡你的通常不是模型原理,而是“老版本代码与新版编译链之间的错配”。拿到一个老仓库,第一件事不要急着读模型代码,而是先把它的依赖版本和环境声明全部列出来,然后逐一对照当前显卡算力重新组合出一套可行的版本矩阵。这套思路跑通了SegFormer,换到其他老仓库同样适用。

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

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

立即咨询