TensorFlow Faster R-CNN ResNet-50 v1 每图仅输出一个边界框
- 运行环境:Google Colaboratory、TPU
- 数据集:自定义单类目标检测数据集,类别数为1
- 基础模型:TensorFlow Object Detection API 提供的 Faster R-CNN ResNet-50 (v1)
- 异常现象:模型训练完成后推理阶段,每张图像仅返回1个边界框,预期可同时检测图像中的多个目标
按排查成本从低到高排序,优先验证无需重训练的配置、代码类问题。
1. 推理侧代码与配置问题
1.1 推理/可视化逻辑仅读取首个输出
导出的模型输出的detection_boxes、detection_scores均为定长张量,按置信度从高到低排序,无效结果的分数会被置为0。如果自定义后处理代码没有遍历全部输出,只取索引为0的第一个框,就会出现仅返回一个框的假象。
排查方式:直接打印完整的detection_scores数组,统计其中分数高于设定阈值的数量。如果存在多个有效分数,修改遍历逻辑即可,示例:
import numpy as np # 筛选所有高于阈值的检测框 valid_idx = np.where(detection_scores[0] > score_thresh)[0] for idx in valid_idx: box = detection_boxes[0][idx] score = detection_scores[0][idx] # 补充画框、输出逻辑
1.2 置信度阈值设置过高
NMS配置中的score_threshold用于过滤置信度过低的框,如果该值设置过高(如0.95以上),大部分目标的预测分数达不到阈值,仅最高分的一个框会被保留。
排查方式:临时将阈值降至0.01重新推理,如果能输出多个框即可确认问题,后续根据验证集的PR曲线调整到合理值即可。
1.3 最大检测数参数被误设为1
检测后处理的batch_non_max_suppression配置块中有两个数量限制参数:
max_detections_per_class:单个类别最多保留的检测框数量max_total_detections:整张图像所有类别最多保留的检测框数量
单类场景下任意一个参数被设为1,都会导致最终仅输出一个框。
排查方式:打开训练用的pipeline.config搜索对应字段,确认数值符合预期。同时需要检查模型导出命令中的--config_override参数,导出时的配置会覆盖训练pipeline中的值,很多配置模板会在这里静默修改限制。
单类检测建议设置为max_detections_per_class: 100、max_total_detections: 100,可根据业务需求调整。
1.4 NMS IoU阈值设置过低
NMS逻辑会保留分数最高的框,删除所有与该框IoU超过阈值的重复框。iou_threshold设置得越低,过滤规则越严格:如果设为0.1~0.2,即使两个相邻目标只有少量重叠,也会被误判为重复框滤除,极端情况仅剩一个框。
排查方式:临时将IoU阈值提升至0.8重新推理,如果框数量恢复正常,根据目标重叠程度调整到0.5~0.7的常用区间即可。
2. Faster R-CNN两阶段管道配置错误
Faster R-CNN为两阶段检测器,第一阶段RPN生成候选框,第二阶段检测头做分类和边界框回归,任意一阶段的数量限制异常都会传导到最终输出。
2.1 RPN候选框数量限制错误
检查pipeline中的first_stage_max_proposals参数,该参数定义RPN最多向第二阶段输送多少个候选框,默认值为200~300。如果被误设为1,第二阶段仅能处理一个候选,最终自然只能输出一个结果。
同时检查RPN专属的first_stage_nms_score_threshold参数,如果该值过高,绝大多数候选框会在第一阶段被直接过滤,仅保留得分最高的一个。
2.2 输入分辨率配置异常
如果pipeline中设置的固定输入分辨率过小,或者保留长宽比的缩放器配置了过低的最大边长,密集小目标会在缩放后无法被识别,仅最大的一个目标能被检测。需要结合目标尺度调整输入分辨率,TPU环境下需保证分辨率为8/128的倍数,满足编译要求。
3. 数据集标注与TFRecord转换错误
3.1 原始标注错误
先抽验包含多目标的训练图像,直接读取原始标注文件(Pascal VOC XML、COCO JSON等),确认:
- 所有目标都有独立标注,无漏标
- 所有目标的类别ID一致,无部分目标被标记为无效类/背景
- 不存在标注工具导出时将同图多框合并的问题
3.2 TFRecord转换逻辑错误
这是自定义数据集非常常见的低级错误:写转换脚本时,遍历单张图的目标框过程中错误地用赋值覆盖列表,而非逐个追加,导致每张图最终仅保留第一个或最后一个标注。
排查方式:直接解析生成的TFRecord文件,抽样10张图检查标注框数量,示例代码:
import tensorflow as tf dataset = tf.data.TFRecordDataset("训练集TFRecord文件路径") for idx, raw_record in enumerate(dataset): if idx >= 10: break example = tf.train.Example() example.ParseFromString(raw_record.numpy()) bbox_count = len(example.features.feature["image/object/bbox/xmin"].float_list.value) print(f"第{idx}张图标注框数量:{bbox_count}")
如果多目标图片的输出均为1,重写转换逻辑,遍历单图标注时初始化空列表、逐个追加框坐标即可。
3.3 类别映射不匹配
TensorFlow OD API默认类别ID从1开始计数,ID 0为背景类。如果标注文件中单类ID设为0,或者.pbtxt类别映射文件的ID与标注不一致,所有标注会被误判为背景,仅偶然产出的随机候选可能被保留,表现为每张图最多一个框。
4. TPU环境特有问题
4.1 推理模型导出配置覆盖
部分TPU专用的配置模板为了适配低算力场景,导出模型时会默认将最大检测数设为1,覆盖训练pipeline中的配置。需要在导出命令中通过--config_override显式指定参数,示例:
python exporter_main_v2.py \ --pipeline_config_path pipeline.config \ --trained_checkpoint_dir ./checkpoints \ --output_directory ./exported_model \ --config_override "model{faster_rcnn{post_processing{batch_non_max_suppression{max_detections_per_class:100 max_total_detections:100}}}}"
4.2 TPU数据分片错误
TPU分布式训练时,如果自定义数据管道的分片逻辑错误,每个TPU核心仅能拿到图像的局部切片而非完整图,模型会被训练为每个切片仅预测一个目标,最终推理时出现异常。需确认使用tf.data自动分片,不要自定义错误的图片裁剪、分片逻辑。
5. 模型训练收敛问题
如果所有配置、数据问题都排除,说明模型退化为单目标预测的退化解,没有学到多目标检测能力,可从以下方向排查:
5.1 训练步数不足或权重恢复异常
- 未加载COCO预训练权重、从头训练的场景,数千步的模型只能学到图像中最显著的目标特征,通常需要十万步以上才能收敛;微调场景也建议保证数千到上万步的训练量。
- 加载预训练权重时检查权重恢复日志,确认RPN、检测头的权重被正确加载,避免因权重维度不匹配导致输出异常。
可通过TensorBoard查看训练损失、验证集mAP的变化趋势,如果损失仍在下降、指标未稳定,延长训练步数即可。
5.2 数据集分布偏置
如果训练集中绝大多数图片仅包含单个目标,多目标样本占比极低,模型会学到“单图仅存在一个目标”的先验偏置,倾向于只输出置信度最高的框。
解决方式:补充多目标场景的标注样本;使用Mosaic拼接类数据增强提升多目标样本占比;调整采样策略,提高多目标图片的采样概率。
5.3 Anchor配置与目标不匹配
默认Faster R-CNN的anchor尺度、宽高比针对COCO通用目标设置,如果自定义目标存在极小、极狭长等极端尺寸特性,默认anchor无法为多数目标生成有效RPN候选,仅最突出的大目标能被检测。
排查方式:统计训练集所有标注框的宽高比、相对图像的尺度占比,对应修改pipeline中anchor_generator块的scales和aspect_ratios参数,匹配目标尺寸范围后重新训练。
- 打印完整
detection_scores数组,排除可视化代码bug - 临时降低置信度阈值、提升NMS IoU阈值,验证框数量变化
- 检查训练pipeline、导出配置中的最大检测数、RPN候选数参数
- 抽样解析TFRecord,排除标注转换错误
- 检查TPU导出、数据管道的适配逻辑
- 结合数据集分布、训练指标调整配置后重新训练
内容的提问来源于stack exchange,提问作者maryam mehboob

