秋天一到山里的蘑菇就像约好了似的往外冒。这几年户外活动的人变多各种登山群里最常见的求助配图就是一张菇盖朝天或者朝地的蘑菇照片配一句这个能吃吗。你放大图片看半天大概率不敢拍板——因为很多可食用和不可食用的品种长得实在太像了。我做的这个项目就是训练一个计算机视觉目标检测模型让它在复杂的野外照片里同时定位出每一朵蘑菇的位置并判断它属于可食用还是不可食用。这个项目的模型配置按惯例可以缩写成一大串Faster-RCNN_RegNetX-4GF_FPN_MS-3x_COCO。拆开读就是两阶段检测框架 Faster R-CNN骨干网络用 RegNetX-4GF颈部结构用 FPN训练时开多尺度MS采用 3x 长周期学习率调度初始权重来自 COCO 预训练模型。这套组合看上去全是术语实际上是一套成熟、稳定、调参成本可控的目标检测方案。下面我把从原理到踩坑的完整过程过一遍给想复现类似项目或者正在做蘑菇识别、农业识别相关工作的朋友做个参考。1. 蘑菇识别为什么要从分类改成检测1.1 实测场景一张照片里从来不只有一朵蘑菇先说一个最直接的原因真实的野外照片和大多数人想象的一朵蘑菇端端正正放在画面中央完全不一样。秋天阔叶林的地面上蘑菇经常是一簇一簇长的你用手机随便一拍画面里可能就有五六朵有的被落叶挡了一半有的还是小菇蕾远处还有几朵快成水渍状的老菇。如果只做一个图像分类模型输入一张图输出一个可食用/不可食用的标签问题立刻就来了模型到底是看到哪朵蘑菇才下这个结论的如果照片里同时有一朵可食用蘑菇和一朵剧毒蘑菇呢分类模型给不出答案它连图里有几朵蘑菇都说不出来。我在项目初期也走过弯路先训了一个二分类网络测试集上准确率看着有 90%拿到真实场景一用就露馅一张图里混着多种蘑菇时模型只挑其中视觉上最扎眼的那一朵来投票其他蘑菇全部被忽略。后来我才意识到蘑菇识别本质上是一个多实例问题不是单标签分类问题。1.2 检测方案带来的额外价值目标检测的输出是若干个(框坐标, 类别, 置信度)三元组每一朵蘑菇都有自己独立的框和置信度。这个差异带来三个直接好处第一是可解释性。模型在哪朵蘑菇上下了什么判断肉眼直接就能在图上看到不用猜。这在实际使用中非常重要哪怕只是给用户看也能让他自己判断模型是不是看错了对象。第二是多实例能力。一张图上无论有几朵蘑菇模型都能逐个给出判定而不是被某朵显眼的蘑菇带偏。这一点对野外照片尤其关键因为蘑菇成簇生长是常态。第三是数据潜力。有了检测框之后后续所有工作都变得好做你可以把每个框裁剪出来送去一个更高精度的细分类模型做物种级识别也可以统计一平方米样方里的蘑菇数量甚至可以通过框的大小粗估子实体的尺寸。对一个想长期迭代的项目来说检测框就是地基。从训练成本看检测模型的数据标注代价确实比分类高但在这个场景里是值得的。我算过一笔账标注一朵蘑菇画一个框大约花 5 到 10 秒几百张图下来也就是两三天的事而一个定位不了蘑菇的分类模型后期返工成本远高于这点标注成本。2. Faster R-CNN RegNetX-4GF FPN 组合的原理与选型理由2.1 RPN 和 RoI 头各干各的活Faster R-CNN 是两阶段检测器的代表作。第一阶段是 RPN区域提议网络它在特征图上铺大量不同大小和长宽比的锚框先粗筛出这里可能有目标的区域并做一次粗略的框回归第二阶段把这些候选区域通过 RoIAlign 统一池化成固定尺寸的特征送入分类分支和回归分支输出最终的类别和精确框。很多人问我蘑菇检测能不能直接用单阶段的 YOLO图个省事我的回答是能用但我最终选了 Faster R-CNN原因出在密集重叠这个蘑菇场景的典型特征上。一群蘑菇挤在一起互相遮挡单阶段模型直接回归框的方式容易把相邻目标糊成一个框或者漏掉被挡住的半边。Faster R-CNN 的第二阶段会对 RPN 提出的每个候选区域单独精修框的质量和定位稳定性明显更好漏检率更低。代价是推理速度慢一些但对识别准确优先的项目来说这笔代价值得付。2.2 RegNetX-4GF 是怎么造出来的一个网络RegNet 这个骨干网络和 ResNet 不一样它不是人工拍脑袋设计出来的而是从一组连续参数中系统搜索出来的网络族。核心参数包括网络深度 d、初始宽度 w0、宽度增长斜率 wa、宽度量化系数 wm、组卷积组宽 g、瓶颈比 b。这些参数共同决定了每一层的通道数应该怎么变化最终生成一整个网络。名字里的4GF指的是这个网络在 224×224 输入下一次前向计算量大约是 4 GFLOPs也就是约 40 亿次浮点运算。而RegNetX表示的是不带 Squeeze-and-Excitation 的简化变体训练更快更适合中低算力项目。在同样的计算量内RegNetX 的精度通常高于同等规模的 ResNet 系列这也是它在各类开源模型库中被大量收录的原因。实际训练时这套骨干的参数一般直接查表抄写就行大致是深度 22、初始宽度 96、宽度斜率 25.4、组宽 48、瓶颈比 1不用自己重新推导。2.3 FPN 和多尺度输入的配合FPN 解决的是蘑菇尺度差异过大的问题。野外照片里一棵松树下远处的蘑菇可能只有十几个像素而特写镜头里的蘑菇能占满画面四分之三。单层特征图没法同时兼顾这两个极端。FPN 的做法是从高到低构建多级特征金字塔每层特征图都有不同的分辨率通过自顶向下的路径和横向连接把高层的语义信息与低层的空间细节融合在一起。小目标在低层高分辨率特征上还留有足够的轮廓信息大目标在高层特征上有更充分的语义判断依据互不耽误。MS 多尺度训练则是训练时的配套手段每次迭代随机把输入图像的短边缩放到 480 到 960 像素之间的某个值让模型反复在不同尺度下观察同类目标。FPN 负责在结构上提供多尺度分支MS 负责在数据流上制造尺度多样性两者配合模型对大蘑菇小蘑菇的适应能力才会真正拉起来。3. 蘑菇数据集构建标注规范比模型选择更影响结果3.1 图片收集与清洗数据来源我主要用了三块野外自采、公开图片检索筛选、以及朋友授权提供的实拍照片。收集时定了几条硬性标准必须是野生、新鲜、完整的子实体菇盖和菌柄尽量完整排除已经烹饪过的、晒干做标本的、加过滤镜的图片。画面中不要出现可识别的人物主体既保护隐私也让模型聚焦在蘑菇本身。同一次采集的连拍照片只随机保留一部分避免大量重复画面把模型带偏。清洗之后我又做了一次去重按感知哈希粗筛掉高度相似的图片最后剩下约 4000 张有效图片其中可食用类约 2100 张、不可食用类约 1900 张。这里有一个重要教训你看到的每一个莫名其妙的误检追溯到底往往都是数据里某张脏图片在作怪。比如我清洗时漏掉了一张松塔的照片模型后期就学会了把松塔当成某种蘑菇。数据清洗花的时间完全不亏。3.2 类目和标注框怎么定两个类别看着简单真正定义起来全是坑。可食用和不可食用在生物分类学上并不是一个干净的二分类。不可食用里既有毒性很强的物种也有无毒但口感差、根本没人吃的物种。我在标注规范里明确写了这两类统一归入non-edible避免不同标注员各执一词。如果你的项目后续要做物种级识别这个类目定义还要再细化。标注框的规范同样要提前定死每个独立的子实体一个框框要同时包住菇盖和菌柄多个蘑菇重叠时尽量分别框实在无法分辨就合并并在备注里标记为 ambiguous。最关键的一条是——所有可见的目标都必须标注。漏标一朵就相当于在教模型这朵蘑菇不重要可以忽略这对目标检测是致命的。标注工具我用的是常见的开源标注工具导出成 COCO 格式的 JSON后续可以直接喂给训练流程。3.3 训练/验证划分与数据增强数据划分这个环节最容易被新手忽略。不能简单地把图片随机打乱分到训练集和验证集因为同一片森林、同一个光线条件下拍的照片高度相似随机划分会导致验证集的虚高——模型并不是真的认识蘑菇只是记住了拍摄环境。我按采集批次划分训练集 70%、验证集 15%、测试集 15%其中同一个采集批次的照片不会同时出现在训练集和验证集里。增强方面我只用了翻转、小角度旋转、随机裁剪和轻微的颜色扰动。旋转角度控制在 20 度以内因为蘑菇有上下方向性转多了标注框的语义会被破坏模型反而学不到有效特征。MS 多尺度训练本身也是一种增强算上之后数据多样性足够支撑这套模型正常收敛。4. 训练过程实录从 COCO 权重迁移到蘑菇数据的配置和关键坑4.1 环境、配置文件和超参数设定训练环境是两台各 16 GB 显存的 GPU框架选用开源检测工具箱 MMDetection 这一套因为它的配置命名和标题几乎一致几乎是开箱即抄。基础配置直接选带 RegNetX-4GF FPN 的 COCO 检测配置然后只改数据集路径和输出类别数。核心配置片段如下_base_ [ ../_base_/models/faster_rcnn_r50_fpn.py, ../_base_/datasets/coco_detection.py, ../_base_/schedules/schedule_3x.py, ../_base_/default_runtime.py, ] model dict( backbonedict( typeRegNet, archregnetx_4gf, out_indices(0, 1, 2, 3), frozen_stages1, init_cfgdict( typePretrained, checkpointopen-mmlab://regnetx_4gf ) ), neckdict( typeFPN, in_channels[96, 192, 384, 768], out_channels256, num_outs5 ), roi_headdict( bbox_headdict( typeShared2FCBBoxHead, num_classes2 ) ) ) dataset_type CocoDataset data dict( samples_per_gpu4, # 两张卡凑成 batch 8 workers_per_gpu4, traindict( typedataset_type, ann_filedata/mushroom/annotations/train.json, img_prefixdata/mushroom/images/ ) ) optimizer dict(lr0.01) lr_config dict( policystep, step[24, 32], warmuplinear, warmup_iters1000 ) runner dict(typeEpochBasedRunner, max_epochs36)这个配置文件的名字faster_rcnn_regnetx-4gf_fpn_ms-3x_coco可以当作一套模板来记忆检测器是 faster_rcnn、骨干是 regnetx-4gf、颈部是 fpn、训练策略是 ms多尺度 3x3 倍长周期学习率调度、预训练权重来自 coco。4.2 学习率和迭代周期的计算很多人直接抄 COCO 的 3x 配置结果发现 27 万迭代根本训不完自己的小数据集。原因是 3x 的 27 万次迭代对应的是 COCO 约 11.8 万张图、batch size 16 算出来的结果。自定义数据集必须按 epoch 换算。我的蘑菇训练集是 2800 张图batch size 8每个 epoch 就是 2800 / 8 350 次迭代。3x 调度代表 36 个 epoch总迭代数就是 350 × 36 12600 次。学习率下降节点在 2/3 和 8/9 处也就是第 24 个 epoch 和 32 个 epoch。学习率本身按线性缩放规则调整COCO 默认在 batch 16 时用 0.02我的 batch 是 8学习率就改成 0.01如果你只有单卡、batch 降到 4就再减半到 0.005。这套换算逻辑对任何自定义检测数据集都适用。4.3 训练日志里值得盯的几个信号训练时我盯的损失分量包括总损失、RPN 的类别/框回归损失、以及第二阶段 RoI 头的类别/框回归损失。整体曲线如下总损失从最初的 1.0 左右快速下降到 0.3之后缓慢降低RPN 的框回归损失会一直有波动这是正常现象不用慌张。我按每 1000 次迭代记录一次验证集指标迭代数total_lossAP50验证集AP验证集30000.3141.228.760000.2156.840.38400第一步降学习率0.1664.146.211200第二步降学习率0.1366.048.312600训练结束0.1266.448.7这里有一个必须强调的坑多数手机原图是 4000×3000 甚至更大训练时默认把长边压到 1333此时画面里原本 15 像素的小蘑菇在特征图上只剩下几个像素模型根本学不动。想让小目标识别能力变强要么调高训练输入分辨率要么在推理阶段做滑窗裁剪。这一点直接决定了后面 AP small 指标的水平。5. 评估结果与错误分析mAP 之外的安全相关指标5.1 标准 COCO 指标实测训练结束后的验证集指标如下指标数值APIoU 0.50:0.9548.7AP5066.4AP7551.8AP small22.6AP medium48.3AP large60.9可食用类别 AP52.4不可食用类别 AP45.0简单解读一下这张表。AP50 和 AP75 的差距说明框的定位精度还行粗重合度下大部分都能对得上但要求精确贴合时掉得比较快。AP small 只有 22.6印证了前面说的小蘑菇没细节问题。两个类别的差距更值得关注可食用类 AP 明显高于不可食用类因为不可食用这个类别内部形态差异极大伞菌、多孔菌、马勃、地星全都被塞进同一个类里模型等于在用一个二分类头去区分相对集中的集合和高度分散的集合难度天然不一样。5.2 错在哪里漏检、串检与小目标问题只看 mAP 不够我还统计了验证集上置信度阈值 0.5 时的检测级混淆情况真实类别 / 预测结果可食用不可食用未检出可食用共 953 框8036189不可食用共 802 框71594137从这个表里能看出三件事。第一漏检集中在不可食用类上达到 137 框因为这些物种形态过于杂乱模型学不过来第二最危险的那一类错误——把不可食用误判成可食用——有 71 框占总预测量的比例虽然不高但对蘑菇识别这种安全敏感场景来说这个数字绝对不能接受第三可食用误判成不可食用也有 61 框这类是假警报损失的是用户体验但不像前一种那样危及生命。具体到图片上看漏检最多的是三种情况落叶背景中颜色相近的小菇、还没撑开菌盖的幼菇、以及被草丛挡住大半的老菇。误检则常见于圆石头、蜗牛壳、卷曲的落叶这类外观接近蘑菇帽的东西。还有一个真问题是我后来检查测试集才发现的白菇类混淆某些可食用口蘑和某些有毒鹅膏在照片上只差鳞片和菌托细节二分类模型没有能力捕捉这种级别的差异。5.3 把模型输出变成参考结论而不是最终结论基于上述错误分析我在后处理上做了三分段决策。置信度大于等于 0.7 时输出模型倾向判定为 X 类置信度 0.7X请专家复核置信度在 0.4 到 0.7 之间时输出不确定不建议作为参考依据低于 0.4 直接判定未检出有效目标。这套策略牺牲了部分果断性换来了不可食用类误判成可食用的大幅收敛。调整阈值后那 71 框危险错误里大部分落入了不确定区间用户的最终决策不会被模型的过度自信带偏。提示任何蘑菇识别模型都不应该输出安全可食用这种绝对结论。模型给出的是倾向性参考最终判断必须由具备专业能力的人完成。这是系统的底线不只是产品文案问题。6. 部署使用与后续扩展模型之外的项目收尾6.1 推理脚本和运行性能部署时我用标准的推理接口封装成函数传入图片路径就能返回检测结果from mmdet.apis import init_detector, inference_detector config_file configs/faster_rcnn/faster_rcnn_regnetx-4gf_fpn_ms-3x_coco.py checkpoint_file work_dirs/mushroom/epoch_36.pth model init_detector(config_file, checkpoint_file) result inference_detector(model, field_photo_2023.jpg)运行性能方面单张 V100 上以 1333 长边推理大约每秒 8 张图纯 CPU 环境单张约 0.6 到 1.0 秒如果换成 TensorRT 并开半精度边缘设备也能做到接近实时的速度。对批量扫描几百张森林照片的需求来说吞吐量远比单张延迟重要所以这个性能是完全够用的。6.2 适合什么场景、不适合什么场景这个模型最适合的场景有三个辅助采集者做野外初筛参考、生态样方里的蘑菇计数与记录、以及科普教育中的照片归档检索。在这些场景里模型的定位是助手它帮你快速缩小范围、标出可疑对象但最终结论由人来做。不适合的场景同样要明确不能用来直接判定能不能吃不能处理幼菇、残缺标本、已烹饪食物这类形态完全不同的样本不能信任它对稀有物种的判断因为那些物种在训练数据里很可能只有零星几张图。模型的能力边界必须写进使用说明里这是负责任的做法。最后说一句我的体会。整个项目真正花时间的不是模型训练而是前面两周的数据清洗和标注规范修改。Faster R-CNN 这套组合之所以适合这类农业与生态识别项目就是因为它稳不像很多新模型那样天天变出了问题能很快在社区里找到答案。如果你打算从这套配置开始做自己的蘑菇检测我建议第一件事不是跑代码而是先把你手头的蘑菇照片按拍摄场景分好批次再开始标框。顺序反了后面所有评估数字都会骗你。