目标检测这个方向,绕不开的一个工程化工具就是mmdetection。我见过太多人卡在同一个地方:模型结构看懂了,论文也读明白了,可一到自己机器上跑训练就各种报错,环境装三天、配置文件改到怀疑人生,最后连第一个 epoch 都没跑起来。这篇东西就是写给这类人的——从零把 mmdetection 部署起来,再完整跑通一次训练,中间那些文档里不写、但实际一定会踩的坑,我尽量一次讲透。
不管你是刚接触目标检测的学生,还是需要把检测模型落到实际业务里的工程师,只要你会基本的命令行操作、能看懂 Python,这篇内容都能直接照着做。我不会只给你一堆命令让你复制粘贴,而是把每一步为什么这么做、参数为什么这么设、报错大概率出在哪,都掰开讲清楚。部署和训练这两件事,本质上是一套连贯的工程流程,拆开讲容易,串起来讲才有价值。
1. 整体部署思路与方案选型拆解
1.1 为什么部署 mmdetection 要先想清楚"版本组合"
很多人装环境的第一反应是"缺什么装什么",结果装到一半发现 torch 版本和 CUDA 对不上,或者 mmcv 和 mmdetection 版本不匹配,然后开始无脑降级、升级,最后环境彻底乱掉。我踩过最惨的一次,是在一台机器上反复折腾了两天,最后发现根本原因是 CUDA 驱动版本太老,跟任何新版 torch 都不兼容,白忙活。
mmdetection 这套工具链有一个非常明确的版本耦合关系:mmdetection 依赖 mmcv,mmcv 依赖 torch,torch 又依赖 CUDA 和显卡驱动。这四层是层层绑定的,任何一层选错,上面全崩。所以正确的做法不是"边装边试",而是先确定最底层,再往上倒推。
具体倒推逻辑是这样的:先看你的显卡驱动支持到哪个 CUDA 版本(用nvidia-smi看右上角的 CUDA Version),这个数字是驱动能支持的最高CUDA 运行时版本,不是说你必须装这个版本,而是不能超过它。然后根据这个上限,选一个稳定的 CUDA 版本(比如驱动支持 12.2,你可以选 11.8 或 12.1,没必要顶格选)。接着根据 CUDA 版本选 torch,再根据 torch 版本选 mmcv,最后根据 mmcv 选 mmdetection。
这里有个关键点必须强调:mmcv 的版本必须和 mmdetection 严格对应。官方在 mmdetection 的requirements里其实写明了兼容的 mmcv 范围,但很多人不看,直接pip install mmcv装最新版,结果 import 就报错。我一般建议直接查 mmdetection 对应版本的官方文档里的兼容表,或者干脆用 mmdetection 官方推荐的组合,别自己发挥。
提示:不要迷信"最新版最好"。目标检测这套生态里,最新版往往意味着文档还没跟上、社区问题还没沉淀,遇到坑只能自己啃源码。选一个发布半年以上、社区讨论充分的稳定版本,能省掉大量排查时间。
1.2 环境隔离:为什么强烈建议用 conda 而不是系统 Python
我见过有人直接在系统 Python 里装 mmdetection,结果把系统自带的包搞崩了,连系统工具都用不了。mmdetection 依赖的包又多又重(torch、mmcv、numpy、opencv 等),版本要求还挑剔,跟系统环境混在一起就是灾难。
用 conda 建独立环境的好处很直接:每个项目一个环境,互不干扰,装坏了直接删掉重建,几分钟的事。而且 conda 能帮你管理 CUDA toolkit 这种非 Python 的依赖,比纯 pip 省心。我的习惯是给每个检测项目单独建环境,命名带上框架和版本,比如mmdet2x,一眼就知道是干什么的。
创建环境的命令本身不复杂,但有个细节值得说:Python 版本别选太新的。mmdetection 对 Python 3.10 以上某些版本的支持是滞后的,选 3.8 或 3.9 最稳。这不是保守,是实测下来报错最少的选择。
conda create -n mmdet2x python=3.9 -y conda activate mmdet2x环境建好之后,先别急着装东西,把 pip 源配一下。默认源在国内拉 torch 这种大包能慢到让你怀疑网络,换成国内镜像源速度能快一个数量级。这一步不算技术活,但直接影响你的部署体验。
1.3 安装顺序的讲究:torch 先行,mmcv 居中,mmdet 收尾
安装顺序这件事,看起来是小事,实际上决定了你会不会遇到"装完 import 报错"的经典问题。正确的顺序是:先装 torch(带 CUDA 版本),再装 mmcv,最后装 mmdetection。
为什么 torch 必须先装?因为 mmcv 在安装时会检测当前环境的 torch 版本,如果 torch 还没装,它可能装一个不带 CUDA 支持的 CPU 版,或者装一个版本不匹配的。等你后面再装 torch,两者就对不上了。所以 torch 一定要第一个装,而且要用官方给的带 CUDA 的安装命令,别用pip install torch这种默认命令——默认命令装的很可能是 CPU 版。
mmcv 的安装是整套流程里最容易出问题的一环。它分mmcv和mmcv-full两个包(新版本里合并了,但老版本区分明显),带 CUDA 算子编译的版本才能发挥完整性能。安装时要么用官方预编译的 wheel(快,但要求 torch 和 CUDA 版本严格匹配),要么从源码编译(慢,但兼容性好)。我一般优先试预编译 wheel,对不上再考虑源码编译。
mmdetection 本身反而是最简单的,从源码 clone 下来,pip install -e .就行。用-e(可编辑模式)安装的好处是,你后面改配置文件、改源码都直接生效,不用重装,调试的时候特别方便。
2. 核心细节解析与实操避坑要点
2.1 显卡驱动与 CUDA 版本的核对方法
这一步是很多教程直接跳过的,但它恰恰是后面所有报错的根源。核对方法很简单,在终端敲:
nvidia-smi输出右上角会显示CUDA Version: XX.X,这个数字代表你的驱动最高支持的 CUDA 运行时版本。注意,这里显示的 CUDA 版本不是你系统里装的 CUDA toolkit 版本,而是驱动能支持的上限。很多人误以为这里显示 12.2 就必须装 CUDA 12.2,其实装 11.8 完全没问题,只要不超过 12.2 就行。
那怎么知道系统里实际装了什么 CUDA?用:
nvcc --version如果这个命令报"command not found",说明系统没装 CUDA toolkit,但这不影响你用 conda 装的 torch——因为 pip/conda 装的 torch 自带 CUDA 运行时,不依赖系统的 toolkit。这一点特别重要,很多人以为必须先装系统级 CUDA 才能用 GPU,其实不是。只有当你需要从源码编译 mmcv 的 CUDA 算子时,才需要系统里有完整的 CUDA toolkit 和 nvcc。
我整理了一个核对清单,部署前对着过一遍,能避免 80% 的环境问题:
| 检查项 | 命令 | 期望结果 | 不满足时的处理 |
|---|---|---|---|
| 驱动是否正常 | nvidia-smi | 能列出显卡和驱动版本 | 重装/升级显卡驱动 |
| 驱动支持的 CUDA 上限 | nvidia-smi右上角 | 记下这个数字 | 选 torch 时不能超过它 |
| 系统 CUDA toolkit | nvcc --version | 有则记版本,无也不影响 | 源码编译 mmcv 时才需要 |
| conda 环境是否激活 | which python | 指向 conda 环境路径 | 重新 activate |
| pip 源是否配置 | pip config list | 有国内源 | 配置镜像源加速 |
2.2 torch 安装:带 CUDA 版本的正确姿势
装 torch 最忌讳的就是随手pip install torch。这个命令装的是默认版本,很可能是 CPU 版,或者 CUDA 版本跟你的驱动对不上。正确做法是去 torch 官网的安装命令生成器,选好你的系统、包管理器(pip 或 conda)、CUDA 版本,它会给你一条精确的命令。
假设你的驱动支持到 CUDA 12.1,想装 torch 2.0 系列,命令大概长这样:
pip install torch==2.0.1 torchvision==0.15.2 torchaudio==2.0.2 --index-url https://download.pytorch.org/whl/cu118这里cu118表示 CUDA 11.8 版本。注意命令末尾的--index-url指向的是 torch 官方的 CUDA wheel 仓库,不是普通的 PyPI,这是关键——只有从这个源装,才能拿到带 CUDA 的版本。
装完之后一定要验证,别装完就以为成了:
import torch print(torch.__version__) print(torch.cuda.is_available()) print(torch.version.cuda)torch.cuda.is_available()返回True才算真正能用 GPU。如果返回False,八成是装成了 CPU 版,或者驱动和 CUDA 版本不兼容。这时候别急着往下走,先把这个问题解决掉,否则后面训练时才发现用不了 GPU,前面的功夫全白费。
注意:
torch.version.cuda显示的是 torch 编译时用的 CUDA 版本,跟nvidia-smi显示的驱动支持上限是两回事。前者是"这个 torch 是用哪个 CUDA 编的",后者是"驱动最多能支持到哪"。只要前者不超过后者,就能正常用。
2.3 mmcv 安装:预编译 wheel 与源码编译的取舍
mmcv 是 mmdetection 的底层计算机视觉库,里面封装了大量 CUDA 算子(比如 NMS、RoIAlign 这些)。这些算子如果没编译好,训练时要么报错,要么性能大打折扣。所以 mmcv 的安装是整套流程里技术含量最高的一步。
安装方式有两种,各有适用场景:
预编译 wheel:官方针对常见的 torch + CUDA 组合提供了预编译好的包,直接 pip 装,几秒钟搞定。缺点是版本组合必须严格匹配,你的 torch 版本、CUDA 版本、Python 版本都得在官方提供的列表里。查对应关系的方法是去 mmcv 的官方文档找兼容表,或者用mim工具自动匹配:
pip install openmim mim install mmcv==2.0.1mim会自动检测你的环境,装匹配的版本,省去手动查表的麻烦。这是我推荐的首选方式。
源码编译:当预编译 wheel 对不上你的环境时(比如你用的是比较新的 torch 或比较冷门的 CUDA 版本),就得从源码编译。编译前必须确保系统里有完整的 CUDA toolkit 和 nvcc,否则编译会失败。编译命令:
pip install mmcv==2.0.1 -f https://download.openmmlab.com/mmcv/dist/cu118/torch2.0/index.html这条命令里的 URL 路径cu118/torch2.0就是版本组合,你得根据自己的环境改。编译过程可能十几分钟到半小时,取决于机器性能。
我个人的经验是:能用预编译 wheel 就别源码编译。源码编译不仅慢,还容易因为系统缺少某些编译依赖(比如 gcc 版本不对、缺 ninja)而失败,排查起来很烦。只有当预编译确实对不上时,才走源码编译这条路。
2.4 mmdetection 安装与验证
mmdetection 本体从源码装,步骤不复杂:
git clone https://github.com/open-mmlab/mmdetection.git cd mmdetection pip install -v -e .-e是可编辑模式,装完之后你改源码里的任何文件都直接生效。-v是显示详细安装日志,出问题时方便定位。
装完验证一下:
import mmdet print(mmdet.__version__)能正常打印版本号就说明装好了。但这里有个隐藏的坑:import 成功不代表能用。有些情况下 import 没问题,但一跑训练就报 CUDA 算子相关的错,本质是 mmcv 的 CUDA 算子没编译好。所以真正的验证是跑一个最小训练,这个后面实操部分会讲。
3. 完整实操流程与训练跑通实录
3.1 数据集准备:COCO 格式的组织方式
mmdetection 默认支持 COCO 格式的数据集,这也是目标检测领域最通用的格式。不管你原始数据是什么样,最后都要转成 COCO 的目录结构。标准结构是这样的:
data/ coco/ annotations/ instances_train2017.json instances_val2017.json train2017/ *.jpg val2017/ *.jpgannotations里放标注 JSON,train2017和val2017里放图片。JSON 的结构遵循 COCO 规范,核心字段包括images(图片信息)、annotations(标注框)、categories(类别)。如果你手上是 VOC 格式(XML 标注),需要写个脚本转成 COCO,转换逻辑不复杂,但要注意坐标格式:VOC 是xmin, ymin, xmax, ymax,COCO 是x, y, width, height,转换时要做减法。
这里有个实操细节值得提醒:图片路径和 JSON 里的file_name必须对得上。我见过有人图片放在train2017里,但 JSON 里的file_name写的是绝对路径或者带了子目录,结果训练时找不到图,报FileNotFoundError。最稳妥的做法是 JSON 里只写文件名(不含路径),让配置文件里的data_root去拼路径。
3.2 配置文件的选择与修改
mmdetection 的配置文件体系是"继承式"的,一个配置文件可以继承另一个基础配置,只覆盖需要改的部分。这个设计很优雅,但对新手不太友好,因为一个最终生效的配置可能分散在好几个文件里,改的时候容易找不到地方。
我的建议是:先选一个跟你的任务最接近的现成配置,然后只改必要的地方。比如你要做 COCO 上的目标检测,选configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py这种,它已经配好了模型结构、训练策略、数据 pipeline,你只需要改数据集路径和类别数。
需要改的核心项就几个:
data_root:数据集根目录metainfo.classes:类别列表,必须跟你的数据集一致num_classes:类别数,改模型头部的分类数ann_file和data_prefix:训练/验证集的标注文件和图片目录batch_size和learning_rate:根据你的显存调整
改类别数这一步特别容易出错。很多人只改了metainfo里的类别列表,忘了改模型头部的num_classes,结果训练时维度对不上,报 shape mismatch。正确的做法是两处都改,或者用继承的方式在子配置里统一覆盖。
3.3 启动训练与关键参数解读
配置改好后,用tools/train.py启动训练:
python tools/train.py configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py \ --work-dir work_dirs/faster_rcnn_exp1--work-dir指定工作目录,训练日志、权重文件、配置文件备份都会存在这里。这个参数强烈建议每次都加,不然默认存到当前目录,多个实验混在一起很难管理。
训练启动后,终端会打印一堆信息,新手容易看懵。我挑几个关键的说:
- Epoch [1][50/xxx]:当前第几个 epoch、第几个 iteration。后面的分母是每个 epoch 的总 iteration 数。
- loss:总损失,训练初期会从很大慢慢降下来,如果一直不降或者变成 nan,说明有问题。
- loss_cls / loss_box:分类损失和回归损失,分开看能判断是分类学不好还是框回归学不好。
- lr:当前学习率,warmup 阶段会从很小线性增长,之后按策略衰减。
判断训练是否正常,最直接的是看 loss 曲线。正常情况 loss 应该整体下降,中间有波动是正常的。如果 loss 一直平着不动,可能是学习率太小或者数据有问题;如果 loss 变成 nan,多半是学习率太大或者数据里有异常值(比如标注框宽高为 0)。
3.4 训练过程中的显存与速度调优
显存不够是训练时最常见的拦路虎。报错通常是CUDA out of memory,意思是显存爆了。解决办法有几个层次:
第一层,减小 batch_size。这是最直接的办法,把batch_size从 8 降到 4 甚至 2,显存占用会明显下降。但要注意,batch_size 变小后学习率也要相应调小,一般按线性比例调。
第二层,开启自动混合精度(AMP)。在配置里加optim_wrapper的type='AmpOptimWrapper',能用半精度计算,显存占用能降 30% 左右,速度还能快一点。这是性价比最高的优化。
第三层,用更小的输入尺寸。配置里的train_pipeline中有Resize操作,把scale从(1333, 800)降到(1000, 600),显存和速度都会改善,代价是小目标的检测精度可能下降。
第四层,梯度累积。如果显存实在太小,batch_size 降到 1 还不够,可以用梯度累积模拟大 batch。配置里设accumulative_counts=4,相当于 4 个小 batch 累积一次梯度,效果接近 batch_size 乘 4。
速度方面,如果训练慢得离谱,先确认是不是在用 GPU。用nvidia-smi看 GPU 利用率,如果一直是 0,说明根本没跑在 GPU 上,回去检查 torch 的 CUDA 是否可用。如果 GPU 利用率忽高忽低,多半是数据加载成了瓶颈,可以调大num_workers(数据加载的进程数),一般设成 CPU 核心数的一半左右。
4. 常见报错排查与独家避坑经验
4.1 环境类报错的快速定位
环境类报错是部署阶段的主力,特征明显,排查有套路。我整理了一张速查表,覆盖了最常见的几种:
| 报错关键词 | 大概率原因 | 解决方向 |
|---|---|---|
No module named 'mmcv' | mmcv 没装或环境没激活 | 确认 conda 环境,重装 mmcv |
undefined symbol | mmcv 和 torch 版本不匹配 | 按兼容表重装匹配版本 |
CUDA out of memory | 显存不足 | 降 batch_size、开 AMP |
torch.cuda.is_available() False | 装成了 CPU 版 torch | 用 CUDA wheel 重装 torch |
nms_cuda相关错误 | mmcv CUDA 算子没编译 | 重装带 CUDA 的 mmcv |
KeyError: 'xxx'在配置里 | 配置项名称写错或版本不兼容 | 对照当前版本文档改配置 |
undefined symbol这个报错特别典型,本质是 mmcv 编译时链接的 torch 版本,跟你运行时用的 torch 版本不一致。比如你装 mmcv 时环境里是 torch 1.13,后来升级到了 torch 2.0,mmcv 的二进制还是按 1.13 编的,就会报这个。解决办法是重装 mmcv,让它按当前 torch 重新编译或匹配。
4.2 训练类报错的排查思路
训练阶段的报错比环境阶段更隐蔽,因为往往不直接告诉你原因。几个高频问题:
loss 变 nan:先看学习率是不是太大,把 lr 降一个数量级试试。如果还不行,检查数据里有没有异常标注(宽高为 0、坐标越界、类别 id 超出范围)。我遇到过一次,是标注里有个框的宽度是负数,导致回归 loss 直接炸了。
精度上不去:训练 loss 降了但验证 mAP 很低,常见原因是训练集和验证集分布差异大,或者数据增强太激进。可以先把数据增强关掉(把 pipeline 里的增强操作注释掉)跑一遍,看基础精度是否正常,再逐步加回来定位问题。
训练速度突然变慢:如果前几个 epoch 正常,后面突然变慢,多半是数据加载的问题。检查num_workers设置,以及数据集里有没有特别大的图片拖慢加载。还有一种可能是磁盘 IO 瓶颈,把数据集放到 SSD 上能明显改善。
4.3 几个文档里不写的实操心得
第一个心得:先跑通再优化。很多人一上来就想调参、换模型、加技巧,结果基础流程都没跑通。我的习惯是先拿官方配置和一小份数据跑通一个 epoch,确认整条链路没问题,再开始改东西。这样出问题时能快速判断是新改的东西导致的,还是环境本身的问题。
第二个心得:日志比终端输出重要。终端输出刷得快,很多信息一闪而过。训练时把日志重定向到文件,或者用--work-dir让日志自动存下来,出问题回去翻日志,比盯着终端强。mmdetection 的日志里会记录每个 iteration 的 loss、lr、显存占用,排查时非常有用。
第三个心得:小数据集验证流程。正式训练前,从训练集里抽 50 张图做一个小数据集,跑 2 个 epoch。如果小数据集能正常跑完、loss 能降,说明流程没问题,再上全量数据。这个习惯帮我省过无数次时间——全量数据跑一半崩了,重来成本太高。
第四个心得:配置文件用继承,别复制粘贴。新手喜欢把基础配置整个复制出来改,结果基础配置更新了,自己的配置还是旧的,各种不兼容。正确做法是用_base_继承,只写要覆盖的部分,这样基础配置的改进能自动继承过来。
提示:遇到实在查不出来的报错,先把完整报错信息(包括 traceback)复制出来,去掉里面的个人路径信息,再去搜。搜的时候带上 mmdetection 版本号和报错关键词,命中率会高很多。很多坑别人已经踩过了,只是你没搜对关键词。
4.4 从训练到推理的衔接
训练跑通后,下一步通常是拿训练好的权重做推理验证。mmdetection 提供了tools/test.py和推理 demo,用训练产出的latest.pth或best.pth就能跑。这里有个细节:推理时的配置必须和训练时一致,尤其是类别数和模型结构,否则加载权重会报维度不匹配。
推理验证通过后,如果要把模型部署到实际业务里,还需要做模型导出(比如转 ONNX)和推理加速。这部分内容展开又是一大篇,但核心思路是:训练阶段追求精度,部署阶段追求速度,两者需要不同的优化策略。训练时用的数据增强、多尺度测试这些技巧,部署时都要去掉,换成固定的输入尺寸和轻量化的后处理。
我个人在实际操作中的体会是,mmdetection 这套工具链的学习曲线,陡峭的部分几乎全在环境部署上,训练本身反而相对顺滑。把版本关系理顺、把环境隔离做好、把报错排查的套路摸清,后面换模型、换数据集都是水到渠成的事。真正拉开差距的,不是你会不会跑一个现成的配置,而是遇到报错时能不能快速定位到根因——这个能力只能靠一次次踩坑积累,没有捷径。