资讯动态

Docker 部署 detectron2:版本矩阵与 Dockerfile 实践

发布时间:2026/10/27 5:15:09 来源:尧图企业网站定制
1. 为什么用 Docker 装 detectron2 这件事值得单独聊detectron2 这个库凡是真正动手装过的人大概率都经历过那种“折腾一下午、最后靠重装系统解决”的绝望。它不是那种pip install一行就能收工的纯 Python 包它的核心算子ROIAlign、可变形卷积、NMS 这些全部是 C/CUDA 手写的安装过程其实是一次现场编译需要 nvcc、gcc、ninja、CUDA toolkit 头文件、PyTorch 的 C 扩展头文件还得祈祷这四者的 ABI 对得上。所以用 Docker 来装 detectron2本质上不是“为了时髦”而是把一次性的、容易失手的编译过程固化成一个可复制、可交付、可回滚的镜像。这篇文章我想把它讲透从版本矩阵怎么钉死到 Dockerfile 怎么写到容器跑起来之后 GPU、数据、显示怎么接进去再到我踩过的那些藏得很深的坑。不管你是刚拿到一张卡准备跑个 Mask R-CNN 的算法新人还是要把训练环境交付给三个同事的工程岗下面这套东西都能直接抄。1.1 detectron2 的依赖链到底卡在哪一环先把“为什么难装”这件事讲清楚否则后面的每个选择都会显得没道理。detectron2 的安装过程里pip只是把源码拉下来真正的重头戏在setup.py build它要调用 nvcc 把.cu文件编译成_C.so。这一步会同时读取三个来源的信息——当前系统的 CUDA toolkit 版本由CUDA_HOME决定、已安装 PyTorch 的 CUDA 版本torch 编译时锁定的版本、GPU 的算力架构决定生成哪些sm_XX目标。这三者只要有一项错位症状就非常典型编译阶段能顺利通过pip install显示成功但一import就报undefined symbol: _ZN3c10...或者ImportError: libcudart.so.11.0: cannot open shared object file。因为编译出来的.so里链接的是 A 版本的运行时符号而 Python 进程加载的是 B 版本的库。这种错误信息对新手极不友好它不会告诉你“版本不匹配”只会甩一串符号名。更麻烦的是驱动侧。宿主机驱动是向下兼容的装新卡驱动就能跑老 CUDA但反过来不行。所以容器里选 CUDA 12.1宿主机驱动却只支持到 CUDA 11.x容器一启动nvidia-smi就报Failed to initialize NVML: Driver/library version mismatch。Docker 的价值就在这镜像一旦构建成功它内部那套 torch CUDA 编译产物是一个自洽的闭环搬到任何一台驱动版本足够新的机器上行为都完全一致。1.2 裸机直装和容器化实际差在哪我两种都干过说点实在的对比。裸机装最大的问题不是装不上而是装上了之后不敢动。比如你机器上原来有个项目锁着numpy1.23、opencv-python4.5detectron2 装完顺手把numpy升到 1.24第二天另一个项目就崩了。反过来降级又可能让 detectron2 的fvcore报错。这种依赖互相踩踏的场面在没有容器之前只能靠 virtualenv 隔离 Python 包但隔离不了系统级的 CUDA toolkit 和 gcc 版本。容器化之后这些全部封在镜像层里。更实际的好处是交付你把镜像打成 tar 包推给同事或者推到内网镜像仓库对方docker load加docker run两条命令就能开工不用再问“你 gcc 是几版”“你 CUDA_HOME 指哪”。代价也有——镜像体积大devel 基础镜像本身就接近 9GB编译完 detectron2 加上各种缓存轻松突破 20GB首次构建时间长GPU 直通需要宿主机装好容器运行时工具。这些代价换取的是可复现性我认为在深度学习场景下非常划算。1.3 三条常见路线先选对再动手动手前先明确你要走哪条路这决定了后面 Dockerfile 的写法。我把常见的三种方案列成表格你可以对着自己的场景直接挑方案大致耗时镜像体积可控性适合谁上游预编译 wheel 精简镜像5~10 分钟6~8GB中受限于上游发布的轮子只想跑推理、快速验证想法devel 基础镜像 源码编译40~90 分钟首次15~25GB高任意改源码、打补丁要改模型结构、加自定义算子直接拉社区现成镜像5 分钟不定低版本是黑盒临时演示、不追究细节我个人的建议是只要你有可能动到 detectron2 的源码或者需要在自己的数据上跑训练就走第二条。第一条看着快但预编译轮子是跟着上游发布节奏走的遇到 torch 版本更新、或者你需要用某个只有主线才有的新特性时就会卡住。第三条我基本不用因为你永远不知道那个镜像里装的是哪个 commit出了诡异报错连排查的起点都找不到。2. 版本矩阵先把对应关系钉死再动手装 detectron2 失败的案例里八成以上是版本没对齐。这一节我把它当成“施工前的图纸”先确定 CUDA、PyTorch、torchvision、detectron2 这四件套的对应关系再动手写任何一行 Dockerfile。跳过这步直接抄网上教程大概率会得到一堆互相矛盾的报错。2.1 CUDA、PyTorch、torchvision 的三角关系PyTorch 的每个发布版本都绑定了一个特定的 CUDA 版本从 wheel 文件名就能看出来比如torch-2.1.0cu118表示它是用 CUDA 11.8 编译的。torchvision 又必须和 torch 主版本号严格对应差一个小版本就可能报RuntimeError: operator torchvision::nms does not exist。detectron2 站在最上层它的要求相对宽松——官方门槛是 PyTorch 1.8、Python 3.7、gcc 5.4要支持 C14CUDA 侧则是 11.1 以上。但“宽松”只体现在最低门槛上实际编译时它必须和当前 torch 的 ABI 完全兼容。我整理了一份实际验证过的组合供你对照CUDAPyTorchtorchvisionPython备注11.82.0.10.15.23.9 / 3.10非常稳社区验证最多11.82.1.20.16.23.10我目前主力组合12.12.1.20.16.23.10新卡40 系推荐12.12.3.10.18.13.11较新需主线源码11.71.13.10.14.13.8老环境兼容还有两个容易被忽略的软依赖numpy 建议锁在 2.0 以下因为 numpy 2.x 改动了一批 C API 和废弃别名某些老版本的第三方库会在 import 阶段直接抛AttributeErrorpycocotools 需要 cython 和 numpy 参与构建所以顺序上要先装 numpy 再装它用一条pip install命令一起给上去最省事。提示上表的组合不是唯一正确答案但它是我在多张卡、多个驱动版本上反复验证过的“安全区”。如果你手上没有强约束直接照抄能省掉大量试错。2.2 用 GPU 算力反推编译目标别让编译器瞎猜这是最容易被跳过、但对编译时间影响巨大的一步。detectron2 的setup.py在没指定架构时会遍历所有支持的算力去生成目标代码从sm_50一直编到sm_90十几套目标乘以几十个 CUDA 源文件编译时间能翻好几倍镜像里也会多出一堆用不上的二进制。正确做法是先查出你目标 GPU 的算力然后通过TORCH_CUDA_ARCH_LIST精确指定。算力可以用一行 Python 拿到import torch print(torch.__version__) print(torch.cuda.get_device_capability()) # 输出形如 (8, 6)拿到(8, 6)之后对应的架构字符串就是8.6。常见卡的对应关系如下卡型档位算力架构字符串入门训练卡T4 一类7.57.5中端卡20 系7.57.5数据中心卡A100 一类8.08.0主流卡30 系、A10 一类8.68.6新卡40 系、L40 一类8.98.9旗舰卡H100 一类9.09.0如果你要在一个镜像里同时支持多种卡用分号隔开比如7.5;8.0;8.6;8.9。但要注意每多一个目标就多一份编译时间镜像也会胖一圈。我的做法是镜像只服务自己团队的卡型跨团队交付时再单独构建一版别指望一个镜像打天下。注意TORCH_CUDA_ARCH_LIST必须在编译阶段就设置好装完再改没用。写进 Dockerfile 的ENV里最省心。2.3 预编译轮子还是源码编译一条判断标准有人会问上游不是提供了预编译的 wheel 吗为什么还要自己编答案是轮子是按固定组合发布的它只覆盖 cu11x torch2.x 这种主流搭配目录结构大致是按cu版本/torch版本/index.html划分的。如果你正好命中一行pip install detectron2 -f 轮子索引地址就能搞定五分钟收工。但只要出现以下任一情况就必须走源码编译你要改detectron2/modeling/下的任何东西你要加自定义 CUDA 算子你的 torch 版本没被轮子覆盖你要用主线某个刚合的 PR 里的修复。判断标准很简单——问自己“我会不会动它的源码”会就编译不会就装轮子。我遇到过一个真实场景某个版本在特定 CUDA 大版本下可变形卷积的 CUDA 源码会因为底层 API 变化而编译失败报的是一堆identifier ... is undefined之类的错误。当时查到的解决办法就是切到包含修复的主线提交重新编译。这种事预编译轮子解决不了只能自己动手。3. 从零写一个能跑的 Dockerfile到这一步版本定了架构列表也定了可以落地了。我会把 Dockerfile 拆成三段来讲基础镜像怎么选、系统依赖装哪些、编译参数怎么调。每一行都不是随便写的我会说明它解决了什么问题。3.1 基础镜像必须选 devel 版本这是硬约束看镜像标签就知道区别带-devel的版本里包含完整的 CUDA toolkit有nvcc编译器不带的是-runtime版本只有运行时库没有编译器。detectron2 要现场编译 CUDA 代码所以基础镜像必须是 devel。你如果图省事选了 runtime 版编译阶段会直接报nvcc: command not found或者在setup.py里检测不到 CUDA 而悄悄降级成 CPU 版本等到推理时才发现_C里根本没有 GPU 算子那时候排查成本就高了。镜像标签的写法一般形如pytorch/pytorch:2.1.2-cuda11.8-cudnn8-devel三个关键信息都在里面torch 版本、CUDA 版本、cuDNN 版本。拉之前建议先在本地docker pull确认标签存在标签写错的话构建会在一开始就失败白白浪费一次等待。还有一个细节构建镜像的机器不需要有 GPU。编译 CUDA 代码只需要 nvcc 和头文件不需要真实设备。这点很重要意味着你完全可以在一个没有卡的构建服务器上打出可以跑 GPU 的镜像。但正因如此setup.py在构建阶段检测不到 GPU可能会跳过 CUDA 编译所以必须显式设置FORCE_CUDA1强制它编译 GPU 部分。3.2 系统依赖清单每一项都有明确用途apt 层的依赖不多但漏一个就报错我按用途列清楚包名作用漏装的表现build-essential提供 gcc/g/makeerror: command gcc failedninja-build加速 C 编译编译慢数倍无报错但耗时暴涨git拉取源码git: command not foundlibgl1OpenCV 的图像显示依赖ImportError: libGL.so.1: cannot open shared object filelibglib2.0-0OpenCV 的基础依赖同上或更隐晦的导入失败pkg-config部分库的编译期探测偶发的 configure 阶段失败libgl1和libglib2.0-0这两个特别值得说。你在本地机器上装 opencv 从来不会遇到问题因为桌面系统早就装好了这些库但容器是纯净的一旦cv2被 import 就会去找libGL.so.1找不到就整个脚本崩掉。而很多人是在跑可视化的时候才第一次import cv2于是在“明明装的都装好了”的错觉里浪费大量时间。提前装掉一劳永逸。Python 侧我建议一次性装齐numpy2、opencv-python-headless、pycocotools、ninja。关于 opencv 选 headless 还是普通版——如果你的容器里需要cv2.imshow弹窗看结果装普通版如果只做读写和推理headless 版体积小、依赖少更合适。我一般两个镜像场景分开构建训练镜像用 headless调试镜像用普通版。3.3 完整 Dockerfile 与构建命令下面这份是我现在在用的骨架注释里写了每段的目的FROM pytorch/pytorch:2.1.2-cuda11.8-cudnn8-devel ENV DEBIAN_FRONTENDnoninteractive # 关键构建阶段没有 GPU强制编译 CUDA 算子 ENV FORCE_CUDA1 # 关键只编目标架构大幅缩短编译时间 ENV TORCH_CUDA_ARCH_LIST8.6 # 编译并行度按构建机内存给内存小就调成 2 或 1 ENV MAX_JOBS4 RUN apt-get update apt-get install -y --no-install-recommends \ build-essential \ ninja-build \ git \ pkg-config \ libgl1 \ libglib2.0-0 \ rm -rf /var/lib/apt/lists/* RUN pip install --no-cache-dir \ numpy2 \ opencv-python-headless \ pycocotools \ ninja WORKDIR /workspace # 把上游仓库地址替换成你实际使用的地址 RUN git clone 上游仓库地址/detectron2.git /workspace/detectron2 \ cd /workspace/detectron2 \ pip install --no-cache-dir -e . # 清掉编译中间产物能省几个 GB RUN cd /workspace/detectron2 rm -rf build/ *.egg-info WORKDIR /workspace CMD [/bin/bash]构建命令docker build -t detectron2:cu118-torch212 .几个点展开说一下。第一pip install -e .是用可编辑模式装的这意味着包目录仍然指向/workspace/detectron2这个源码目录你可以直接在容器里改代码、改配置重启 Python 进程就生效不用重新安装。这在调模型结构的时候极其方便。代价是这个目录绝对不能删删了包就废了import会直接报错。这是很多人做镜像瘦身时踩的坑——清理/workspace求体积小结果把源码删了。第二rm -rf build/这一步要放在安装之后。build/目录里是编译产物的中间态_C.so已经在装的过程中被复制到包目录里了删掉 build 不影响运行。不清的话镜像能多出好几个 GB 的中间文件。第三MAX_JOBS别贪大。编译 detectron2 的 CUDA 文件时单个进程的内存占用能到 2~3GBMAX_JOBS8在 16GB 内存的构建机上会直接把 OOM killer 招来表现是 gcc 被 kill、编译静默失败然后你在几十分钟后发现装了个半成品。宁可慢一点也别让编译进程被系统杀掉。3.4 想再省点体积多阶段构建的思路如果你对镜像体积敏感比如要推到带宽有限的镜像仓库可以用多阶段构建。思路是第一阶段用 devel 镜像编译第二阶段用 runtime 镜像把编译好的 Python 包和依赖从第一阶段拷过来。因为 detectron2 运行时并不需要 nvcc只需要那几个.so文件和 CUDA 运行时库。不过我要提醒一句这条路省下的体积大概是 3~4GB但踩坑概率显著上升。拷site-packages时容易漏掉.pth文件、dist-info目录或者漏掉 detectron2 依赖的某个共享库最后表现为“装是装上了一 import 就提示找不到某个 so”。除非你对镜像体积有硬性指标否则我不建议折腾直接清理中间产物就够用了。4. 运行容器GPU、数据、显示三件事接对镜像构建成功只是第一步容器跑起来之后还有一堆事GPU 直通要通、数据要挂进去、共享内存要够大、要可视化还得接 X11、文件权限还得对得上。这一节我把启动命令拆成参数逐个讲。4.1 启动命令逐参数拆解最基础的启动长这样docker run -it --rm \ --gpus all \ --shm-size16g \ -v $(pwd)/data:/workspace/data \ -v $(pwd)/code:/workspace/code \ -w /workspace \ detectron2:cu118-torch212--gpus all是 GPU 直通的核心它依赖宿主机上装好的容器运行时工具。如果你只加了这个参数但工具没装容器能起来但里面torch.cuda.is_available()返回False而且nvidia-smi提示命令不存在。这时候别怀疑镜像去宿主机装工具链装完重启 Docker 服务。--shm-size16g我强烈建议显式指定。Docker 默认的/dev/shm只有 64MB这个值对普通程序够用但 PyTorch 的 DataLoader 在多进程模式下num_workers 0会通过共享内存在进程间传递张量。数据一多/dev/shm写满你会看到一个非常莫名其妙的报错Bus error (core dumped)或者unable to write to shared memory。这个错误跟代码逻辑毫无关系但能让人排查一下午。给 8~16GB 基本能覆盖常规训练超大 batch 或者多卡场景可以给到 32GB。替代方案是--ipchost直接复用宿主机的共享内存命名空间简单粗暴但在多用户共享的机器上有一定隔离风险。-v挂载就不用多说了但有个细节值得提代码目录也建议挂进去而不是 COPY 进镜像。这样你改代码不用重新构建训练脚本、配置文件、notebook 全在宿主机上编辑容器只负责跑。4.2 数据挂载与数据集根目录的约定detectron2 有个内置机制它会先读环境变量DETECTRON2_DATASETS指向的目录找不到再去datasets/相对路径下找。这个设计在多机器、多挂载点的场景下特别有用因为你可以把所有数据集统一放在一个盘上容器里用环境变量指过去docker run -it --rm \ --gpus all \ --shm-size16g \ -e DETECTRON2_DATASETS/workspace/datasets \ -v /mnt/bigdisk/datasets:/workspace/datasets:ro \ -v $(pwd)/code:/workspace/code \ -w /workspace \ detectron2:cu118-torch212注意那个:ro只读挂载。这是我在一次事故之后加上的习惯——某次训练脚本里有个路径拼错了把输出写进了数据集目录覆盖掉了几百张标注图。只读挂载能让这类错误在第一秒就抛异常而不是在几小时后发现数据坏了。数据集永远只读输出目录单独挂一个可写的这个规矩值得刻在脑门上。还有个性能细节如果数据集在机械盘上而容器训练时num_workers开得比较大IO 会成为瓶颈。这时候把数据先拷到 SSD 或者用内存盘做缓存训练速度能有肉眼可见的提升。我在一个目标检测任务上实测过num_workers8时机械盘和 SSD 的差异能到 30% 左右的吞吐差距。4.3 需要弹窗看结果时X11 怎么接做检测任务可视化cv2.imshow或者 matplotlib 弹窗是最直观的。容器里没有显示服务得把宿主机的 X11 socket 挂进去xhost local:docker docker run -it --rm \ --gpus all \ --shm-size16g \ -e DISPLAY$DISPLAY \ -v /tmp/.X11-unix:/tmp/.X11-unix:rw \ -v $(pwd)/code:/workspace/code \ -w /workspace \ detectron2:cu118-torch212xhost local:docker是给本地 docker 用户组放行用完记得xhost -local:docker收回来。这一步在纯服务器上没有桌面环境当然做不了那种场景我一般改成把结果画好存成图片或者视频文件挂载到宿主机上看比折腾远程显示稳定得多。顺带说个实战经验可视化用的镜像和训练用的镜像最好分开。可视化要普通版 opencvheadless 版没有imshow训练要 headless 版依赖少、不占显存。混在一起会让镜像变大也没什么实际收益。4.4 文件权限容器里生成的文件别变成 root 所有这是个容易被忽略但很烦人的问题。Docker 默认以 root 身份跑容器训练产出的 checkpoint、日志、可视化图在宿主机上看全部属于root:root你作为普通用户改不了也删不掉每次都得sudo。解决办法是启动时把当前用户 ID 透传进去docker run -it --rm \ --gpus all \ --shm-size16g \ -u $(id -u):$(id -g) \ -v $(pwd)/code:/workspace/code \ -w /workspace \ detectron2:cu118-torch212但这里有个副作用容器里没有对应的用户记录~会解析失败某些库尤其是 matplotlib会因为找不到 home 目录而报警告甚至崩溃。折中办法是在 Dockerfile 里预先建好一个同 UID 的用户或者运行时通过-e HOME/tmp绕过去。我自己的习惯是建镜像时就创建一个dev用户UID 设成 1000大多数情况下和宿主机用户对得上对不上再临时用-u覆盖。5. 装完别急着跑训练先做三层验证镜像构建完成、容器也起来了很多人会直接上训练脚本然后被一堆报错淹没分不清是环境问题还是代码问题。我的做法是分三层验证逐层往上走每层通过再进下一层。这样一旦出问题排查范围可以立刻缩小。5.1 第一层CUDA 与 PyTorch 是否握手成功先确认最底层是通的python -c import torch; print(torch:, torch.__version__); print(cuda available:, torch.cuda.is_available()); print(device count:, torch.cuda.device_count())三行输出每行都对应一个判断点。torch.__version__应该带cu118之类的后缀如果后缀是cpu说明基础镜像选错了或者是 pip 装了个 CPU 版覆盖掉了。cuda available: True说明驱动和运行时通了如果是False问题在宿主机侧——容器运行时工具没装、驱动版本太低、或者--gpus参数没加。device count应该等于你实际挂进去的卡数少于预期的话检查CUDA_VISIBLE_DEVICES和--gpus是不是打架了。再补一句nvidia-smi它能直接看到驱动版本和显存占用是最快的“物理层”体检。5.2 第二层detectron2 的 C 扩展是否真的编出来了这一层是最多翻车的地方# 注意一定要在 detectron2 源码目录之外执行 cd /workspace python -c import detectron2; print(detectron2:, detectron2.__version__); from detectron2 import _C; print(_C loaded ok)这里有个非常隐蔽的坑我必须单独强调不能在 detectron2 的源码根目录下执行这段命令。因为 Python 的导入优先级是当前目录优先它会把你源码里的detectron2/文件夹当成包来导入而那个文件夹里的_C是编译前的状态于是报ImportError: cannot import name _C。你会以为编译失败了其实是站在了错误的位置。这个错误我见过太多人反复踩包括我自己。显式from detectron2 import _C这个动作是在验证编译产物真的能加载。如果这行过了说明.so里的符号、CUDA 运行时依赖全部正常。5.3 第三层跑一次真实推理端到端确认前两层过了说明环境是健康的。最后一层用一个小推理脚本确认整条链路包括权重下载、模型构建、前向计算import torch from detectron2.config import get_cfg from detectron2 import model_zoo from detectron2.engine import DefaultPredictor import cv2 cfg get_cfg() cfg.merge_from_file( model_zoo.get_config_file(COCO-Detection/faster_rcnn_R_50_FPN_3x.yaml) ) cfg.MODEL.WEIGHTS model_zoo.get_checkpoint_url( COCO-Detection/faster_rcnn_R_50_FPN_3x.yaml ) cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST 0.5 cfg.MODEL.DEVICE cuda predictor DefaultPredictor(cfg) img cv2.imread(/workspace/code/test.jpg) outputs predictor(img) print(检测到目标数:, len(outputs[instances])) print(类别:, outputs[instances].pred_classes.tolist()) print(框坐标:, outputs[instances].pred_boxes.tensor.shape)这段脚本会触发一次权重下载所以构建镜像的机器或容器运行环境需要能访问外网。如果内网环境建议提前把权重文件下好挂进去然后cfg.MODEL.WEIGHTS直接指向本地路径。我第一次在内网跑的时候就是卡在这看着光标转了十分钟才反应过来是在等下载超时。跑通这一步基本可以宣告环境就绪。如果还要跑训练建议再用小数据集跑 100 次迭代把数据加载、增强、loss 反传、保存 checkpoint 整条链路都过一遍。训练路径和推理路径调用的代码分支不一样推理通不代表训练通尤其是 DataLoader 那块num_workers 0时才能暴露共享内存的问题。6. 常见报错速查与排查思路下面这张表是我这些年攒下来的按报错原文归类出问题先在这里找能省掉大量搜索时间。6.1 报错速查表报错关键字根因处理方式ImportError: libGL.so.1缺 OpenCV 系统依赖装libgl1与libglib2.0-0ImportError: cannot import name _C编译失败或在源码目录下执行换个目录执行回看编译日志undefined symbol: _ZN...torch 与编译时的 CUDA ABI 不一致重装 torch 或重编 detectron2保证版本一致nvcc: command not found基础镜像选了 runtime 版换成-devel标签重建RuntimeError: Not compiled with GPU support编译时没设FORCE_CUDA1加环境变量重新编译Bus error (core dumped)/dev/shm空间不足--shm-size16g或--ipchostFailed to initialize NVML宿主驱动版本与镜像 CUDA 不匹配升级宿主机驱动或降 CUDA 版本torch.cuda.is_available()为 False容器运行时工具未装或未加--gpus查宿主机工具链检查启动参数AttributeError: module numpy has no attribute ...numpy 2.x 与老库不兼容锁numpy2Killed编译阶段并行编译导致内存耗尽降MAX_JOBS到 1 或 2opencv弹窗失败没接 X11 或用了 headless 版挂载 X11 socket换普通版 opencv6.2 几个特别隐蔽的坑值得单独拎出来坑一可编辑安装之后把源码目录删了。pip install -e .建立的是一个软链接式的引用包的实际位置还在源码目录。有人为了减小镜像体积RUN rm -rf /workspace/detectron2结果容器一import就崩。这个问题特别难查因为pip list里明明显示 detectron2 已安装。判断方法python -c import detectron2; print(detectron2.__file__)看路径是不是指向一个不存在的目录。坑二预编译轮子和源码编译混装。先pip install了轮子后来又pip install -e .装了源码版两个版本的文件混在site-packages里import时加载的是哪个不确定。表现是“有时候能跑有时候不能跑”改个代码行为就变了。解决办法是彻底卸载干净再装pip uninstall -y detectron2之后确认site-packages里没有残留目录再重新装。坑三CUDA_VISIBLE_DEVICES和--gpus叠加。--gpus device0,1传给容器的是两张卡的可见列表但容器内部如果又设了CUDA_VISIBLE_DEVICES0程序只能看到一张卡。两层映射叠加时0指的是映射后的第一张卡不是物理 0 号卡。这个语义容易搞混尤其在多卡机器上跑单卡实验时经常出现“明明指定了 2 号卡实际用的是 0 号”的情况。只用一层控制要么在 Docker 参数上限制要么在容器内用环境变量别两边都设。坑四编译缓存没清镜像无故膨胀。编译完 detectron2 之后源码目录下会有一个build/文件夹里面的中间产物体积跟最终产物差不多大甚至更大。不清的话一个本该 12GB 的镜像能给你干到 20GB。清理时机要放在pip install -e .之后、镜像层提交之前而且要和安装写在同一个RUN指令里否则被删的文件仍然存在于上一层体积一点没省。坑五num_workers和单卡小显存的耦合。训练时把num_workers开到 16 图省事结果除了吃共享内存还会和主进程抢 CPU导致 GPU 利用率反而上不去。我一般的经验值是4 * GPU数量具体还要看数据增强的复杂度和 CPU 核数。这个参数不是越大越好得实测调。7. 我现在的工作流和几个实用习惯写了这么多最后分享一些我沉淀下来、不太好从文档里查到的东西。镜像我是分层构建的。第一层叫base只装系统依赖和 torch这一层几个月才动一次第二层叫runtime在 base 上装 detectron2 和业务依赖开发期频繁变。这样改业务代码时不用重编所有东西构建时间从几十分钟降到两三分钟。千万不要把所有东西塞进一个RUN每次改一行就全量重来心态会崩。打标签我习惯用语义化命名比如detectron2:cu118-torch212-v3把 CUDA、torch 和内部迭代号都写进去。用latest是自找麻烦三个月后你根本不知道跑着的这个镜像是哪一版。有条件的话同时推一份到内网仓库本地硬盘挂了还能捞回来。还有一个小技巧在镜像里预置一个check_env.py把前面那三层验证打包成一个脚本。容器起来了先跑它输出全绿再开工。这个脚本我放在/usr/local/bin/下团队里新人接手第一件事就是跑它能挡掉八成“环境问题”的求助。最后一点体会Docker 把环境问题从“每次部署都要重来一遍”变成了“一次性投入、长期复用”。头一两次构建花的那一个多小时会在后面每一次换机器、每一次交付、每一次回滚里连本带利地赚回来。真正难的不是写 Dockerfile而是想清楚自己要锁死哪几个版本、要支持哪些卡型、要把哪些东西留在镜像里哪些挂载出去。这些问题在动手之前想明白整个流程会顺得多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑