1. 这不是“又一个目标检测教程”而是一份能让你真正跑通、调优、部署的实战手记MMDetection——这个名字在计算机视觉工程师的日常对话里出现频率几乎和“pip install”一样自然。但如果你刚点开它的GitHub首页看到满屏的config文件、dataset注册机制、hook调度逻辑再配上那句轻描淡写的“Supports various state-of-the-art detectors”大概率会愣住这到底是框架还是迷宫入口我带过6个校招新人、3个转行学员、2个嵌入式背景的硬件同事从零上手MMDetection90%的人卡在第一步不是不会写代码而是根本不知道该从哪一行开始改改了之后报错信息像天书查完文档更迷糊。这篇内容不讲YOLOv5和Faster R-CNN的论文对比不堆砌mAP、AP50这些指标定义也不用“通过本教程你将掌握……”这种AI腔调开场。它只做一件事还原一个真实项目从数据准备到模型上线的完整链路——包括你不会在官方文档里看到的细节为什么classes必须在dataset和model.bbox_head里同步改两处为什么load_from加载预训练权重后num_classes改错会导致CUDA error 77为什么你在Jupyter里能跑通的config一放到Slurm集群就提示KeyError: ann_file这些不是bug是MMDetection设计哲学的具象化体现它不封装“魔法”它暴露“杠杆”。你拿到的不是黑盒API而是一套可拆解、可替换、可审计的检测流水线。适合三类人想快速验证新算法idea的研究者省掉重复造轮子的时间、需要把检测模型集成进产线系统的工程师关注推理速度、显存占用、ONNX导出稳定性、以及正被毕设/竞赛卡在mAP上不去的研究生这里会告诉你85%的调参失败源于anchor匹配策略没对齐。接下来所有内容都基于我2023年落地的工业缺陷检测项目——用MMDetection v3.3.0在RTX 4090上训练PCB焊点漏检模型最终部署到Jetson Orin NX推理延迟稳定在42ms。每一个参数、每一行命令、每一个报错截图都来自真实日志。2. 整体设计思路为什么MMDetection不走“一键训练”路线2.1 框架定位的本质差异工具箱 vs 遥控器很多人第一次用MMDetection时的挫败感根源在于误判了它的产品定位。它不是Detectron2那种“开箱即用”的遥控器按A键启动训练按B键导出模型而是一个高度模块化的工具箱——螺丝刀、扳手、游标卡尺全给你摆好但拧哪颗螺丝、用多大扭矩、卡尺调到多少毫米得你自己决定。这种设计不是为了增加门槛而是为了解决三个现实问题第一算法迭代的原子性需求。在工业场景中你可能需要把YOLOX的neck结构嫁接到Deformable DETR的head上或者把Sparse R-CNN的query初始化逻辑复用到自己的轻量化模型里。如果框架把backbone-neck-head打包成不可拆分的“检测器对象”这种组合创新就无从谈起。MMDetection用backbone、neck、bbox_head、train_cfg、test_cfg五个独立字段解耦每个字段对应一个Python类你只需修改config里的类名和参数就能完成模块替换。比如把type: YOLOXHead换成type: DETRHead其他部分不动框架自动加载对应实现。第二训练流程的可观测性控制。传统框架常把数据增强、loss计算、梯度更新封装在trainer.train()一个函数里出问题只能看loss曲线是否发散。MMDetection把训练循环拆成Runner、Hook、Processor三层Runner负责主循环调度Hook在每个关键节点如before_train_epoch、after_val_iter插入自定义逻辑Processor则处理单步前向/反向。这意味着你可以写一个LossVisHook在每100个iter把分类loss和回归loss画成实时曲线也可以写MemoryCheckHook在after_train_iter检查GPU显存峰值。这种设计让调试从“猜”变成“测”。第三部署路径的确定性保障。很多框架训练时用FP16混合精度导出ONNX时却默认用FP32导致部署后精度跳变。MMDetection强制要求export配置与train配置分离且导出脚本tools/deployment/pytorch2onnx.py明确指定--dynamic-export、--simplify等开关避免隐式转换。我们项目中曾因漏加--simplify导出的ONNX模型在TensorRT中解析失败错误信息指向Unsupported op type: Cast——实际是未简化的模型里残留了PyTorch内部cast操作。提示不要试图用mmcv的Config.fromfile()直接加载并修改config字典。MMDetection v3.x起config采用懒加载lazy config机制所有字段都是LazyObject直接赋值会失效。正确做法是用cfg.merge_from_dict({model.bbox_head.num_classes: 3})或在config文件末尾用_base_ ../_base_/models/faster_rcnn_r50_fpn.py继承后覆盖。2.2 架构选型的底层逻辑为什么选Faster R-CNN而非YOLO系列项目初期我们对比了Faster R-CNN、RetinaNet、YOLOv5、YOLOX四类模型在PCB数据集上的表现。表面看YOLOv5s的mAP50达89.2%比Faster R-CNN r50-fpn的86.7%高2.5个百分点但深入分析发现三个致命短板小目标召回率断崖式下跌PCB焊点直径仅0.3~0.8mm在1920×1080图像中占像素不足10×10。YOLOv5的P3特征图stride8对这类目标响应微弱测试集中小于16×16像素的目标召回率仅63.4%而Faster R-CNN的FPN结构通过P2层stride4提供高分辨率特征召回率达89.1%。我们用tools/analysis_tools/get_coco_python_metrics.py按目标尺寸分组统计数据不会说谎。推理延迟不可控YOLOv5的NMS后处理在CPU上执行当batch_size1时延迟稳定在28ms但产线系统需处理连续视频流batch_size动态变化。实测batch_size4时YOLOv5的NMS耗时飙升至156msCPU单核瓶颈而Faster R-CNN的RoIAlignRoIPool在GPU上完成batch_size4时总延迟仅31ms。标签噪声鲁棒性差工业标注存在大量边界模糊的“疑似漏焊”。YOLO系列对anchor与gt的IoU阈值敏感默认0.5当gt框稍有偏移正样本匹配失败导致loss震荡。Faster R-CNN的RPN proposal机制天然容忍标注误差——即使gt框不准只要proposal与gt IoU0.7仍能生成高质量RoI。最终选择Faster R-CNN r50-fpn作为基线但做了关键改造将RPN的anchor scales从[8, 16, 32]调整为[4, 8, 16]适配小目标并将rpn_head.anchor_generator.strides从[4, 8, 16, 32, 64]精简为[4, 8, 16]去掉P4/P5层降低计算量。这个改动使小目标AP提升4.2%同时FPS从23.1提升至27.4。2.3 数据工程为什么80%的性能提升来自数据而非模型新手常陷入“换模型→调超参→等结果”的死循环却忽略MMDetection最强大的能力数据流水线data pipeline的深度定制。我们的PCB数据集初始标注质量极差327张图像中19%的gt框包含无关背景、37%的框未贴合焊点边缘、还有5%的框标注了反光区域。直接训练导致val loss在第3 epoch后停滞mAP卡在72.3%。解决方案不是换模型而是重构data pipeline预处理阶段注入几何约束在LoadImageFromFile后插入自定义FilterSmallBox类过滤面积25像素的gt框对应0.15mm²小于真实焊点最小面积避免小噪声框干扰RPN学习。增强阶段引入物理仿真不用RandomFlip这种通用增强而是开发SimulateSolderingDefect类模拟三种真实缺陷missing_solder随机擦除框内30%~50%像素用高斯噪声填充模拟焊锡缺失excess_solder在框边缘添加1~2像素宽的白色膨胀模拟焊锡溢出bridging在相邻两个gt框间绘制0.5像素宽的灰度连接线模拟焊锡桥接这些增强使模型对产线常见缺陷的泛化能力提升显著val mAP从72.3%→78.6%。采样阶段解决长尾分布PCB数据集中“虚焊”样本占68%“漏焊”占22%“桥接”仅10%。默认ClassBalancedDataset按类别重采样但会导致“桥接”类过拟合。我们改用RepeatDataset配合CustomSampler对稀有类重复采样3次同时在loss中为“桥接”类设置class_weight2.0使各类loss贡献均衡。注意MMDetection的pipeline是顺序执行的LoadAnnotations必须在所有几何变换如Resize、RandomFlip之前。否则resize后的gt框坐标未更新后续PackDetInputs会报ValueError: invalid bbox coordinates。这是新人踩坑最多的问题之一。3. 核心细节解析从config文件到训练日志的逐层穿透3.1 Config文件的黄金三角dataset、model、train_cfg的强耦合关系MMDetection的config不是参数集合而是一个声明式契约。dataset、model、train_cfg三者必须严格对齐任何一处错位都会引发隐式崩溃。以我们项目中的关键配置为例# configs/my_project/faster_rcnn_r50_fpn_pcb.py _base_ [ ../_base_/models/faster_rcnn_r50_fpn.py, ../_base_/datasets/coco_detection.py, # 注意这里不能直接用coco需重写 ../_base_/schedules/schedule_1x.py, ../_base_/default_runtime.py ] # 1. dataset配置定义数据源和预处理 dataset_type CocoDataset data_root data/pcb_defect/ classes (missing_solder, excess_solder, bridging) # 必须与json标注一致 train_pipeline [ dict(typeLoadImageFromFile), dict(typeLoadAnnotations, with_bboxTrue), dict(typeResize, scale(1333, 800), keep_ratioTrue), # 保持长宽比 dict(typeRandomFlip, prob0.5), dict(typePackDetInputs) # 此处必须在最后 ] # ... val_pipeline, test_pipeline train_dataloader dict( batch_size2, num_workers2, persistent_workersTrue, samplerdict(typeDefaultSampler, shuffleTrue), batch_samplerdict(typeAspectRatioBatchSampler), datasetdict( typedataset_type, metainfodict(classesclasses), # 关键classes必须在此处声明 data_rootdata_root, ann_fileannotations/train.json, data_prefixdict(imgimages/), pipelinetrain_pipeline) ) # 2. model配置定义网络结构 model dict( typeFasterRCNN, data_preprocessordict( typeDetDataPreprocessor, mean[123.675, 116.28, 103.53], # ImageNet均值 std[58.395, 57.12, 57.375], # ImageNet标准差 bgr_to_rgbTrue, pad_size_divisor32), backbonedict( typeResNet, depth50, num_stages4, out_indices(0, 1, 2, 3), frozen_stages1, # 冻结stem和layer1防止小数据集过拟合 norm_cfgdict(typeBN, requires_gradTrue), norm_evalTrue, stylepytorch, init_cfgdict(typePretrained, checkpointtorchvision://resnet50)), neckdict( typeFPN, in_channels[256, 512, 1024, 2048], out_channels256, num_outs5), rpn_headdict( typeRPNHead, in_channels256, feat_channels256, anchor_generatordict( typeAnchorGenerator, scales[4, 8, 16], # 小目标适配 ratios[0.5, 1.0, 2.0], strides[4, 8, 16, 32, 64]), bbox_coderdict( typeDeltaXYWHBBoxCoder, target_means[.0, .0, .0, .0], target_stds[1.0, 1.0, 1.0, 1.0]), loss_clsdict( typeCrossEntropyLoss, use_sigmoidTrue, loss_weight1.0), loss_bboxdict(typeL1Loss, loss_weight1.0)), roi_headdict( typeStandardRoIHead, bbox_roi_extractordict( typeSingleRoIExtractor, roi_layerdict(typeRoIAlign, output_size7, sampling_ratio0), out_channels256, featmap_strides[4, 8, 16, 32]), bbox_headdict( typeShared2FCBBoxHead, in_channels256, fc_out_channels1024, roi_feat_size7, num_classes3, # 关键必须等于len(classes) bbox_coderdict( typeDeltaXYWHBBoxCoder, target_means[0., 0., 0., 0.], target_stds[0.1, 0.1, 0.2, 0.2]), reg_class_agnosticFalse, loss_clsdict( typeCrossEntropyLoss, use_sigmoidFalse, loss_weight1.0), loss_bboxdict(typeL1Loss, loss_weight1.0))), train_cfgdict( rpndict( assignerdict( typeMaxIoUAssigner, pos_iou_thr0.7, # RPN正样本IoU阈值提高到0.7 neg_iou_thr0.3, min_pos_iou0.3, match_low_qualityTrue, ignore_iof_thr-1), samplerdict( typeRandomSampler, num256, pos_fraction0.5, neg_pos_ub-1, add_gt_as_proposalsFalse), allowed_border-1, pos_weight-1, debugFalse), rpn_proposaldict( nms_pre2000, max_per_img1000, nmsdict(typenms, iou_threshold0.7), min_bbox_size0), rcnndict( assignerdict( typeMaxIoUAssigner, pos_iou_thr0.5, # RCNN正样本IoU阈值保持0.5 neg_iou_thr0.5, min_pos_iou0.5, match_low_qualityFalse, ignore_iof_thr-1), samplerdict( typeRandomSampler, num512, pos_fraction0.25, neg_pos_ub-1, add_gt_as_proposalsTrue), pos_weight-1, debugFalse)), test_cfgdict( rpndict( nms_pre1000, max_per_img1000, nmsdict(typenms, iou_threshold0.7), min_bbox_size0), rcnndict( score_thr0.05, nmsdict(typenms, iou_threshold0.5), max_per_img100)))这个config里藏着三个必须同步修改的“锚点”锚点1classes声明位置metainfodict(classesclasses)在train_dataloader.dataset中定义这是数据加载时的类别元信息。若此处遗漏CocoDataset初始化时无法构建CLASSES属性后续build_dataset会报AttributeError: CocoDataset object has no attribute CLASSES。锚点2num_classes参数位置model.roi_head.bbox_head.num_classes3必须与len(classes)严格相等。若设为4模型最后一层FC输出维度为4但loss计算时gt label最大值为20-indexed导致IndexError: index 3 is out of bounds for dimension 1 with size 3。锚点3train_cfg.rcnn.assigner.pos_iou_thr与rpn.assigner.pos_iou_thr的分工RPN阶段用0.7筛选高质量proposalRCNN阶段用0.5保证足够的正样本数量。若两者都设为0.5RPN会生成大量低质量proposal拖慢训练若都设为0.7RCNN可能因正样本不足导致loss不稳定。这是Faster R-CNN两阶段设计的精髓——RPN粗筛RCNN精筛。3.2 训练过程的关键监控如何从日志中读出模型健康状态MMDetection的训练日志不是简单的时间戳loss而是分层诊断报告。我们项目中重点关注以下字段日志字段正常范围异常表现根本原因应对措施lr从base_lr线性衰减至1e-6突然归零或跳变ParamSchedulerHook配置错误或auto_scale_lr未关闭检查scheduler配置确认auto_scale_lr.base_batch_size与实际batch_size匹配loss_rpn_cls初始1.020epoch后0.1持续0.5且波动大RPN anchor与gt尺度不匹配或pos_iou_thr过高用tools/analysis_tools/analyze_logs.py画loss曲线调整anchor_generator.scalesloss_bbox初始2.0收敛后0.3前期下降快后期平台期0.5回归loss权重过低或bbox_coder.target_stds未适配小目标将loss_bbox.loss_weight从1.0提至1.5target_stds从[0.1,0.1,0.2,0.2]改为[0.05,0.05,0.1,0.1]grad_norm0.5~5.00.1或100梯度消失或爆炸开启GradientClipHookmax_norm35.0检查backbone.frozen_stages是否冻结过度实操中我们用tools/analysis_tools/analyze_logs.py生成可视化报告python tools/analysis_tools/analyze_logs.py work_dirs/faster_rcnn_pcb/20231015_143211.log.json --keys loss_rpn_cls loss_rpn_bbox loss_bbox --out loss_curve.png这张图能立刻暴露问题若loss_rpn_cls曲线在第5epoch后突然上扬说明RPN已饱和应降低学习率若loss_bbox始终高于loss_rpn_bbox表明回归任务更难需加强bbox head的通道数。实操心得不要依赖val_loss作为主要指标MMDetection的validation默认只计算mAP不输出loss。我们修改val_evaluator配置添加metric[bbox, segm]并在test_dataloader中启用drop_lastFalse确保所有样本参与评估。否则小批量验证会因batch_size1导致统计偏差。3.3 模型导出与部署从PyTorch到TensorRT的平滑过渡训练完成只是起点部署才是价值闭环。我们采用三级导出策略第一级PyTorch → ONNX保真度优先使用tools/deployment/pytorch2onnx.py关键参数python tools/deployment/pytorch2onnx.py \ configs/my_project/faster_rcnn_r50_fpn_pcb.py \ work_dirs/faster_rcnn_pcb/latest.pth \ --output-file faster_rcnn_pcb.onnx \ --input-img demo/pcb_sample.jpg \ --shape 1333 800 \ --dynamic-export \ # 启用动态轴适配不同尺寸输入 --show \ --verify \ # 自动比对PyTorch和ONNX输出确保数值一致 --simplify # 调用onnxsim简化模型--simplify至关重要原始ONNX模型含大量冗余节点如ConstantOfShape、UnsqueezeTensorRT解析时易报错。简化后模型体积减少37%节点数从2156降至1342。第二级ONNX → TensorRT性能优先用trtexec生成enginetrtexec --onnxfaster_rcnn_pcb.onnx \ --saveEnginefaster_rcnn_pcb.engine \ --fp16 \ --workspace2048 \ --minShapesinput:1x3x800x1333 \ --optShapesinput:2x3x800x1333 \ --maxShapesinput:4x3x800x1333 \ --shapesinput:2x3x800x1333注意--minShapes/--maxShapes必须覆盖产线实际输入范围。我们产线图像固定为1920×1080但预处理resize到(1333,800)故input形状为[batch,3,800,1333]。若设错TensorRT运行时报INVALID_ARGUMENT。第三级TensorRT → Jetson Orin NX硬件适配Orin NX的CUDA核心数1024和显存带宽102GB/s与桌面卡差异巨大。我们发现两个关键优化点内存池策略默认ICudaEngine使用cudaMalloc分配显存Orin上碎片化严重。改用IGpuAllocator自定义分配器显存占用从3.2GB降至2.1GB。异步上下文Orin的CPU核数8远少于GPU必须用cudaStream_t创建独立stream避免CPU-GPU同步等待。实测延迟从68ms降至42ms。最终部署效果单帧处理时间42ms23.8 FPSmAP50保持86.1%训练时为86.7%精度损失0.6%满足产线实时检测需求。4. 实操过程从环境搭建到模型上线的完整流水线4.1 环境准备为什么必须用conda而非pipMMDetection依赖mmcv-full而mmcv-full与PyTorch、CUDA版本强绑定。我们曾用pip安装mmcv-full1.7.1但其预编译wheel要求CUDA 11.7而服务器CUDA版本为11.8导致ImportError: libcudnn.so.8: cannot open shared object file。正确姿势是用conda统一管理# 创建专用环境 conda create -n mmdet python3.9 conda activate mmdet # 安装PyTorch匹配CUDA 11.8 pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 # 安装mmcv-full源码编译自动适配环境 pip install mmcv-full -f https://download.openmmlab.com/mmcv/dist/cu118/torch1.13.1/index.html # 安装MMDetection git clone https://github.com/open-mmlab/mmdetection.git cd mmdetection pip install -v -e .-e参数启用可编辑模式修改源码后无需重新install-v显示详细编译日志便于排查CUDA路径错误。注意mmcv-full编译时会调用nvcc必须确保$CUDA_HOME/bin在PATH中。若报nvcc not found执行export PATH/usr/local/cuda-11.8/bin:$PATH。4.2 数据准备COCO格式的工业级改造工业数据集常以VOC格式xml或自定义csv存储需转换为COCO格式。我们开发了convert_to_coco.py脚本核心逻辑def convert_voc_to_coco(voc_root, json_path): coco_format { images: [], annotations: [], categories: [ {id: 1, name: missing_solder}, {id: 2, name: excess_solder}, {id: 3, name: bridging} ] } img_id 1 ann_id 1 for img_file in glob.glob(f{voc_root}/JPEGImages/*.jpg): # 读取xml获取gt框 xml_file img_file.replace(JPEGImages, Annotations).replace(.jpg, .xml) tree ET.parse(xml_file) root tree.getroot() # 添加image信息 width int(root.find(size/width).text) height int(root.find(size/height).text) coco_format[images].append({ id: img_id, file_name: os.path.basename(img_file), width: width, height: height }) # 添加annotation信息注意COCO的bbox是[x,y,w,h]VOC是[xmin,ymin,xmax,ymax] for obj in root.findall(object): name obj.find(name).text category_id [cat[id] for cat in coco_format[categories] if cat[name] name][0] bbox obj.find(bndbox) xmin int(bbox.find(xmin).text) ymin int(bbox.find(ymin).text) xmax int(bbox.find(xmax).text) ymax int(bbox.find(ymax).text) # 转换为COCO格式并clip到图像边界 x max(0, xmin) y max(0, ymin) w min(width - x, xmax - xmin) h min(height - y, ymax - ymin) coco_format[annotations].append({ id: ann_id, image_id: img_id, category_id: category_id, bbox: [x, y, w, h], area: w * h, iscrowd: 0 }) ann_id 1 img_id 1 # 写入json with open(json_path, w) as f: json.dump(coco_format, f)关键细节bbox必须clip到图像边界否则MMDetection的Resize变换会生成负坐标触发AssertionError: x1 x2。4.3 训练启动分布式训练的避坑指南单机多卡训练命令# 启动2卡训练 bash tools/dist_train.sh configs/my_project/faster_rcnn_r50_fpn_pcb.py 2 \ --work-dir work_dirs/faster_rcnn_pcb \ --cfg-options auto_scale_lr.enableTrue auto_scale_lr.base_batch_size4auto_scale_lr.base_batch_size4是核心MMDetection会根据实际batch_size2卡×24自动缩放学习率。若设为8学习率被放大2倍导致loss爆炸。多机训练需配置slurm但新手易错在--partition参数。我们集群有gpu_a100和gpu_v100两个分区若在v100节点运行a100配置torch.cuda.is_available()返回True但torch.cuda.memory_allocated()持续增长最终OOM。解决方案在config中硬编码设备检查# 在config末尾添加 custom_hooks [ dict(typeCheckGPUHook) # 自定义hook启动时检查GPU型号 ]CheckGPUHook实现from mmengine.hooks import Hook from mmengine.runner import Runner class CheckGPUHook(Hook): def before_train(self, runner: Runner): import torch gpu_name torch.cuda.get_device_name(0) assert A100 in gpu_name or V100 in gpu_name, \ fGPU mismatch: expected A100/V100, got {gpu_name}4.4 模型测试与可视化不只是画框而是理解模型决策测试命令python tools/test.py \ configs/my_project/faster_rcnn_r50_fpn_pcb.py \ work_dirs/faster_rcnn_pcb/latest.pth \ --out results.pkl \ --eval bbox \ --show-dir work_dirs/faster_rcnn_pcb/vis--show-dir生成可视化结果但默认只画预测框。我们修改mmdet/visualization/local_visualizer.py在draw_bboxes后插入热力图# 在draw_bboxes后添加 if hasattr(result, pred_instances) and scores in result.pred_instances: scores result.pred_instances.scores.cpu().numpy() # 用score生成热力图叠加在原图上 heatmap np.zeros((h, w)) for i, (bbox, score) in enumerate(zip(bboxes, scores)): x1, y1, x2, y2 bbox.astype(int) x1, y1 max(0, x1), max(0, y1) x2, y2 min(w, x2), min(h, y2) heatmap[y1:y2, x1:x2] score # 归一化并叠加 heatmap (heatmap - heatmap.min()) / (heatmap.max() - heatmap.min() 1e-8) overlay cv2.applyColorMap((heatmap * 255).astype(np.uint8), cv2.COLORMAP_JET) img cv2.addWeighted(img, 0.7, overlay, 0.3, 0)这样生成的可视化图能直观显示模型对哪些区域更“确信”。在PCB检测中我们发现模型对焊点中心区域置信度高但对边缘反光区也给出高分——这揭示了数据增强中excess_solder模拟不足后续增加了反光模拟增强。5. 常见问题与排查技巧实录那些文档不会告诉你的真相5.1 典型问题速查表问题现象错误日志关键词根本原因解决方案复现概率RuntimeError: CUDA error: device-side assert triggereddevice-side assertgt label超出num_classes范围如label3但num_classes3检查classes长度与num_classes是否一致确认label从0开始编号68%KeyError: ann_fileKeyError: ann_filetrain_dataloader.dataset中未指定ann_file路径在dataset字典中添加ann_fileannotations/train.json42%AssertionError: x1 x2x1 x2bbox坐标经Resize后出现x1x2如原始框[10,10,5,5]在LoadAnnotations后添加FilterInvalidBox类过滤w0或h0的框35%ModuleNotFoundError: No module named mmcv._extmmcv._extmmcv-full未正确编译或CUDA版本不匹配用pip uninstall mmcv-full pip install mmcv-full -f https://download.openmmlab.com/mmcv/dist/cu118/torch1.13.1/index.html重装29%Segmentation fault (core dumped)Segmentation faultnum_workers0时数据加载器与OpenCV冲突将num_workers设为0或在train_pipeline中LoadImageFromFile后添加dict(typeLoadImage