1. 在16.04上做这件事前先想清楚三件事1.1 为什么Ubuntu 16.04折腾GLIBC和Python生态的年代差先说个现实问题Ubuntu 16.04发布于2016年自带GLIBC 2.23而mmdetection的现代版本和配套编译工具链很多编译产物都依赖更高的GLIBC版本。之前我在一台老机器上直接照搬新教程结果在import torch那一步就报GLIBC_2.29 not found当时整个人是懵的。这个报错的意思很直白你想用的PyTorch或mmcv编译时是基于新版系统的动态库链接的GLIBC符号在你的系统里根本不存在。解决办法不是去网上找一堆不安全的二进制替换libc而是老老实实选择一套和系统年代匹配的版本组合。同样的问题也出现在Python层面。16.04系统自带的Python 3.5已经非常老了很多新版本的mmdet、mmcv都不再兼容Python 3.5。我在实际配置中把它锁定在Python 3.7不追新原因后面会展开说。所以入手16.04装mmdetection的第一认知就是不要试图全上最新版本而是要构建一套“古董但自洽”的版本矩阵。谁先接受“老系统配老版本”这个现实谁就能少踩三分之二的坑。适合看这篇内容的人我总结一下手里正好有16.04的老机器、或者因为显卡驱动原因只能留在16.04、再或者在某些工控设备上必须用老内核的人。如果你的机器可以随便装20.04以上系统我建议你直接上新系统别回头。1.2 显卡、驱动、CUDA三角约束先搞定在装任何软件之前先把显卡型号确认清楚。在16.04这种老系统上显卡驱动版本和CUDA版本是互相锁死的顺序不能乱。我当时的做法是三步走。第一步用lspci | grep -i nvidia查看显卡型号第二步去NVIDIA官网看该型号支持的最新驱动版本第三步根据驱动版本反推CUDA版本。实际中遇到过这样一个例子一台机器是GTX 1080 Ti驱动版本390.xx这个驱动最高只支持到CUDA 9.2左右。这意味着你最多也就用PyTorch 1.0左右的版本mmdetection基本没法跑。这时候就得考虑升级驱动到440以上才能用CUDA 10.2。显卡驱动版本可用的CUDA版本可搭配的PyTorch建议390.xxCUDA 9.2及以下PyTorch 1.0以下不推荐跑mmdetection418/430CUDA 10.1/10.2PyTorch 1.5~1.8450CUDA 11.0/11.3PyTorch 1.8~1.10这里额外提一句如果你连显卡都没有只想用CPU跑一跑示例那也请保持CPU版PyTorch的版本对应关系不要混装。1.3 是不是非16.04不可很多人的挣扎在于周围教程都是18.04、20.04自己这台机器偏偏是16.04。这时候我通常给出的建议排序是能上20.04就上20.04这是最优解一套新版本组合随便配。因为老显卡或软件依赖无法升级系统的选择16.04 老版本组合也就是本文的主题。既不能换系统、又想用新功能——可以考虑用Docker容器在Docker里跑新版Ubuntu镜像。但这对显卡直通和nvidia-container-toolkit有要求而且16.04主机内核老Docker的gpu参数兼容性需要专门测试我试下来反倒是裸装更快。我个人的倾向是如果确定留在16.04就干脆把心气放平把本文推荐的老版本组合当标准答案不要再惦记mmdetection最新的3.0版本。2. 驱动、CUDA与conda虚拟环境一锅端式的准备方案2.1 禁用nouveau安装NVIDIA驱动16.04自带的nouveau开源驱动和NVIDIA闭源驱动之间的冲突是环境安装的第一道坎。不把它禁掉装完驱动重启后大概率出现黑屏或循环登录。操作顺序是这样的编辑配置文件sudo vim /etc/modprobe.d/blacklist-nouveau.conf写入下面两行内容blacklist nouveau options nouveau modeset0更新内核配置并重启sudo update-initramfs -u sudo reboot重启后先确认nouveau没被加载lsmod | grep nouveau如果没有任何输出说明禁用成功。接着去NVIDIA官网下载对应显卡的runfile安装包直接.run文件安装比apt源里的版本更可控特别是老环境。安装命令chmod x NVIDIA-Linux-x86_64-450.142.run sudo ./NVIDIA-Linux-x86_64-450.142.run -no-x-check -no-nouveau-check装完用nvidia-smi验证。如果显示驱动版本和显存信息说明驱动没问题。这一步看似基础但我见过太多人跳过后在mmdet训练时报各种CUDA相关崩溃回头查才发现是驱动层的cuda版本根本不兼容。2.2 CUDA与cuDNN安装尽量用runfile而不是deb包CUDA和cuDNN的安装方式会影响后期mmcv编译的顺利程度。16.04上用deb包安装有个问题它会把驱动也一并更新掉导致你辛辛苦苦装好的驱动版本被覆盖。所以我的方案是驱动用runfile装CUDA也用只装工具库的方式不动驱动。CUDA下载页里选择Linux、x86_64、Ubuntu 16.04版本对应的runfile。假设我们选用CUDA 10.2wget https://developer.download.nvidia.com/compute/cuda/10.2/Prod/local_installers/cuda_10.2.89_440.33.01_linux.run sudo sh cuda_10.2.89_440.33.01_linux.run安装时注意在提示是否安装驱动时选No只安装CUDA Toolkit。然后设置环境变量把下面内容加到~/.bashrcexport PATH/usr/local/cuda-10.2/bin:$PATH export LD_LIBRARY_PATH/usr/local/cuda-10.2/lib64:$LD_LIBRARY_PATHcuDNN的安装更简单把下载的cudnn-10.2-linux-x64-v8.2.1.32.tgz解压然后把文件内容拷贝到CUDA目录下tar -xzvf cudnn-10.2-linux-x64-v8.2.1.32.tgz sudo cp cuda/include/cudnn*.h /usr/local/cuda/include/ sudo cp cuda/lib64/libcudnn* /usr/local/cuda/lib64/这一步完成后记得同步一下软链接否则编译时可能出现libcudnn.so.8 not found。这是老系统上非常经典的坑因为16.04不太会主动刷新动态库缓存。2.3 conda环境与Python 3.7给所有依赖建一个隔离区到这一步我强烈建议装Anaconda或Miniconda绝对不要直接用系统Python。16.04自带Python 3.5且很多系统工具链依赖它直接覆盖会出大问题。Miniconda对老系统更友好安装命令wget https://repo.anaconda.com/miniconda/Miniconda3-py37_4.12.0-Linux-x86_64.sh bash Miniconda3-py37_4.12.0-Linux-x86_64.sh安装时一路yes最后初始化conda。创建虚拟环境conda create -n mmdet python3.7 -y conda activate mmdet为什么锁Python 3.7因为mmdetection 2.x时代的官方README和大量预编译的mmcv wheel都明确支持Python 3.7而且3.7在AGES工具箱里兼容性最高。整个mmdet全家桶对这个Python版本支持得最稳实际测试中3.8也可以但在16.04的GLIBC限制下3.7的轮子最听话。3. mmcv与mmdetection的版本匹配系统成败的分水岭3.1 版本对应关系别信任何单点教程mmcv和mmdetection的版本匹配关系极其严格不同大版本之间API有变化连配置字段都可能改过名。在16.04上我最终是装成了mmdetection 2.25.1 mmcv-full 1.7.5 PyTorch 1.10.0 CUDA 10.2这一组合。这个搭配的思考逻辑是Python 3.7 PyTorch 1.10 CUDA 10.2在老驱动能用、且版本不要太老之间取了平衡。mmcv-full 1.7.5这个版本提供丰富的预编译包选项支持PyTorch 1.10。mmdetection 2.25.1是2.x系列的后期版本功能足够且配置组织方式成熟。这里还需要看一个关键对应表mmcv与mmdet官方支持关系mmdetection版本mmcv全版本2.25.1mmcv-full 1.5.1, 1.8.02.18.1mmcv-full 1.4.6, 1.7.02.10.0mmcv-full 1.3.8, 1.4.0别小看这些区间mmcv版本装高了或装低了轻则警告重则直接报AttributeError或KeyError比如mmdet里用到的某个函数在mmcv新版本里被改名了。3.2 安装PyTorch与mmcv-full的完整命令先装PyTorch 1.10.0对应CUDA 10.2直接用conda安装最省事conda install pytorch1.10.0 torchvision0.11.0 torchaudio0.10.0 cudatoolkit10.2 -c pytorch -y这里有个易错点如果你预先下载了本地wheel安装必须保证wheel是CUDA 10.2编译的。我曾经贪快用了pip install torch1.10.0默认源结果拉到了CPU版本训练时torch.cuda.is_available()一直返回False排查了半天才发现基础装错了。接着装mmcv-full。有两个选择一是直接用官方预编译wheel二是源码编译。在16.04上如果预编译wheel能匹配到你的Python和CUDA版本那是最快路径如果报找不到对应的wheel就老老实实从源码编译。预编译方式pip install mmcv-full1.7.5 -f https://download.openmmlab.com/mmcv/dist/cu102/torch1.10/index.html源码编译方式预编译失败时的备选git clone -b v1.7.5 https://github.com/open-mmlab/mmcv.git cd mmcv pip install -r requirements/optional.txt MMCV_WITH_OPS1 FORCE_CUDA1 pip install -e .注意源码编译时MMCV_WITH_OPS1这个环境变量不能省不然只会装纯Python版本后续运行检测时会报一些算子在自定义CUDA扩展上缺失的错误。3.3 验证安装是否真的可用装完后我建议不止做import测试而是直接跑一次简单测试看CUDA是否可用python -c import torch; print(torch.__version__, torch.cuda.is_available()) python -c import mmcv; print(mmcv.__version__) python -c import mmdet; print(mmdet.__version__)同时在Python里跑一个小算子import torch from mmcv.ops import nms boxes torch.tensor([[0, 0, 10, 10, 0.9]], dtypetorch.float32).cuda() keep nms(boxes, torch.tensor([0.5]).cuda(), 0.4) print(keep)如果这段输出正常说明整个CUDA编译链路都是通的可以进入数据准备阶段了。很多人在这一步直接卡住有的报torch.cuda.is_available() False有的报nms算子加载失败。前者多半是PyTorch版本和CUDA不匹配后者多半是mmcv未安装带ops的版本。4. 数据集准备与配置文件改动训练前最后的硬骨头4.1 COCO格式目录结构就是规则mmdetection默认推荐COCO格式数据集。如果你手上是VOC或自建数据集推荐先转成COCO格式因为后续一切可视化脚本、评测接口都基于这个格式省得绕路。目录组织如下data/ coco/ annotations/ instances_train2017.json instances_val2017.json train2017/ 000001.jpg 000002.jpg ... val2017/ 000001.jpg ...自建数据集时你需要写一个脚本把标注转换为COCO json最少包含categories、images、annotations三个节点。提醒一句images里的id和annotations里的image_id必须一致否则训练时数据加载会直接报找不到图片的index这个初学最容易漏。4.2 配置文件继承体系从base到sub-configmmdetection的配置成熟度很高“拿来改”比“从零写”靠谱太多。它的逻辑是先有基础配置再逐层覆盖字段。实际训练时主要改三个文件configs/_base_/datasets/coco_detection.py改数据路径、训练图片尺度、数据增强参数。configs/_base_/schedules/schedule_1x.py改学习率、step、总epoch数。configs/_base_/default_runtime.py改checkpoint保存间隔、日志配置、工作目录。比如我们训练SSD或Faster R-CNN通常会创建一个新配置文件_base_ [ ../_base_/datasets/coco_detection.py, ../_base_/schedules/schedule_1x.py, ../_base_/default_runtime.py ] model dict( typeFasterRCNN, backbonedict(...), ... ) data dict( samples_per_gpu2, # 每个GPU的batch大小 workers_per_gpu2, # 每个GPU的DataLoader线程数 ) optimizer dict(lr0.02/8) # 如果samples_per_gpu调整过学习率也要等比调整我当时自作主张把samples_per_gpu调成4却没动学习率结果损失直接NAN后来参照官方文档里“linear scaling rule”把学习率调小才恢复。4.3 数据增强与类别匹配中的隐藏要求模型头和数据集类别数必须匹配。COCO默认是80类如果你用自建数据集只有3个类别必须在配置里显式覆盖model dict( roi_headdict( bbox_headdict( num_classes3, )), ... )这个问题很隐蔽因为不覆盖也不会报错默认80类会和你的标注id对不上导致所有框都往错误的类别上套。我在某次实验里完全没注意训练了很久mAP一直很低直到可视化检测结果才发现类别全乱这属于白跑几个小时的经典失误。数据增强方面mmdet的默认pipeline已经包含随机翻转、随机缩放、多尺度resize。在16.04老机器上如果显存不宽裕建议把img_scale从(1333, 800)改成(1000, 600)训练速度会提升明显mAP损失在接受范围内。5. 训练流程实操从命令行到恢复断点5.1 单卡训练命令与常见参数一切就绪后进入训练conda activate mmdet cd /path/to/mmdetection python tools/train.py configs/faster_rcnn_r50_fpn_1x_coco.py --work-dir work_dirs/my_experiment参数说明--work-dir保存日志和checkpoint的目录不设置时会自动生成在work_dirs/下。--cfg-options临时修改配置比如--cfg-options optimizer.lr0.01适合做小实验时用。--resume-from从指定checkpoint恢复。--auto-resume自动从最近一次中断点恢复这个参数在2.x版本里很实用。启动后日志会输出每个epoch的loss、bbox_mAP等指标。这里有个小习惯启动训练后别急着离开先等两三个iteration确认loss在合理范围比如0.5到2之间震荡如果一开始loss就NAN立刻CtrlC回去查配置别心疼这几分钟。5.2 多卡训练与GPU选择如果你的老机器插了多张显卡可以用官方脚本bash tools/dist_train.sh configs/faster_rcnn_r50_fpn_1x_coco.py 2 --work-dir work_dirs/my_experiment其中2是GPU卡数。但是16.04机器多为老平台PCIe带宽和CPU都可能是瓶颈。实测下来双卡加速往往达不到2倍甚至会因为通信开销拖慢。所以老机器上我反而推荐先单卡跑通再考虑分布式的test-time augmentation或推理加速。显存不够是另一个高频问题。如果训练时报CUDA out of memory优先做三件事把samples_per_gpu降到1。把img_scale进一步缩小。开启梯度累积在配置里使用optimizer_config dict(grad_clipNone, typeGradientCumulativeOptimizerHook, cumulative_iters4)。这个梯度累积技巧在显存受限时堪称救星等效增大batch size又不炸显存。5.3 训练中途崩溃怎么续训练跑着跑着因为机房断电、SSH断开、显存溢出被杀进程是常态。mmdetection的断点续训机制很完善做法是python tools/train.py configs/faster_rcnn_r50_fpn_1x_coco.py --resume-from work_dirs/my_experiment/epoch_10.pth它会从epoch 10继续训练优化器状态、学习率调度都会一并恢复。如果不想手动指定--auto-resume会自动找最新的checkpoint。迭代过程中还建议定期把配置文件复制到work_dirs里留档比如cp configs/faster_rcnn_r50_fpn_1x_coco.py work_dirs/my_experiment/否则后续想回溯当时实验用的哪套参数会非常被动。评测模型时用python tools/test.py configs/faster_rcnn_r50_fpn_1x_coco.py work_dirs/my_experiment/epoch_12.pth --eval bbox注意--eval bbox只评测检测框如果要评测分割结果要改成segm参数用错了不会报错但输出metric缺失容易误判。6. 我在16.04上踩过的坑编译、训练中的典型报错与排查链路6.1 编译报错别只看最后一行要看完整日志源码编译mmcv时最常见的报错是error: command gcc failed with exit status 1这个错误信息太笼统90%以上的人卡在这里。我当时的排查思路是往上翻日志找到真正的根源。通常有几种情况缺少python头文件需要先conda install python-dev或conda install python3.7-dev。缺少CUDA头文件确认/usr/local/cuda/include/cuda.h存在。gcc版本太老如果CUDA 10.2自带驱动的约束官方要求gcc 5.4没问题但如果升级到CUDA 115.4就不够了需要gcc 7.5。说到gcc16.04默认gcc是5.4。解决办法是apt安装gcc-7sudo add-apt-repository ppa:ubuntu-toolchain-r/test sudo apt update sudo apt install gcc-7 g-7 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 70 sudo update-alternatives --install /usr/bin/g g /usr/bin/g-7 70注意如果换gcc版本之前已经编译过的CUDA工具链不一定需要重装但重新编译mmcv时一定要确认gcc --version是预期版本。6.2 训练中出现NAN的排查链路NAN是训练阶段最常见的脏数据类问题在16.04老环境里还多了一个特殊性硬件稳定性降低某个PCIe插槽接触不良或电源供电不足也可能随机产生NAN。我的排查顺序是先排除数据如果随机抽样几百张验证图片loss都在合理范围那数据本身没问题。再看学习率线性缩放规则batch减半那么lr也要减半否则NAN概率极高。检查BN层的num_batches_tracked低batch size下BN统计不稳定可以在配置里norm_cfg dict(typeBN, requires_gradTrue)改成SyncBN或减小lr进一步观察。硬件层面查dmesg | grep -i error看有没有GPU相关报错用nvidia-smi -pl 200给老卡降一点功耗墙训练稳定性会好很多。这里强调一下环境越老越不能把NAN直接归因到算法要按层、按数据、按硬件一步步隔离才能找到真凶。6.3 GPU利用率上不去、CPU爆满的老机器优化16.04老机器CPU核数普遍不多训练时GPU利用率经常只在30%左右波动这是因为DataLoader在疯狂做图像解码和resizeCPU成了瓶颈。解法data dict( samples_per_gpu2, workers_per_gpu4, # 适当调大worker数 )workers_per_gpu从2调到4或6通常能让GPU利用率明显回升。但也不要盲目开太大worker太多会造成内存占用飙升老机器内存不够时反而触发swap速度暴跌。我的一般经验是内存16G配2~4个worker32G以上配8个也没问题。还有一个容易被忽略的点可以用torch.backends.cudnn.benchmark True在输入尺寸固定时开启cudnn自动调优对小数据集训练有明显加速效果。老机器上跑训练最忌讳看着GPU没吃满就着急换大batch很多时候瓶颈不在GPU而在数据管线和IO。先诊断再优化才是正道。最后再分享一个实际操作中的体会Ubuntu 16.04 mmdetection这套配置如果你能接受“不追新、版本锁死、一切按部就班”的心态其实是可以很稳定地跑起来的。我多次在重装环境后照着一份自制的版本清单装基本半小时内能恢复到昨天的工作状态。建议你也把最终成功的版本组合和安装命令写成一份备忘而不是依赖记忆。老系统的容错率低但确定性高一次配好持续使用体验反而比每次升级都要折腾的人舒畅得多。
阅读完成 · 觉得有帮助?