YOLO 推理结果解读:置信度阈值、NMS 调参与工程部署建议

YOLO 推理输出结构

标准 YOLO API 输出格式(以 Ultralytics 为例):

{
  "class": "vehicle",
  "class_id": 1,
  "confidence": 0.91,
  "bbox": {
    "x1": 156.15, "y1": 487.68,
    "x2": 653.12, "y2": 690.16,
    "width": 496.97, "height": 202.47
  }
}

判断结果质量时关注两点:

  • 置信度分布:主要目标是否集中在 0.7 以上
  • 框的空间分布:是否存在高度重叠的框

四类常见问题

1. 重复框(NMS 不净)

同一辆车被检测 2~3 次,框坐标相近但不完全重合。原因是 IOU 阈值偏低,NMS 未能合并相似框。

# 默认 0.45 偏低,调高到 0.55~0.60
results = model(img, iou=0.55)

2. 低置信度噪声

大量 confidence < 0.4 的框混入结果,尤其是行人、远处小目标。默认 conf=0.25 会保留大量噪声。

# 推荐生产阈值
results = model(img, conf=0.45)

效果:去掉垃圾框,结果更干净,后续处理负担更小。

3. 小目标识别差

行人、远处目标置信度普遍偏低(< 0.5),框也不稳定。可能的原因:

  • 输入分辨率不够(模型内部将图缩放到 640×640 后,远处目标只占几个像素)
  • 训练数据中远距离目标样本少

优化方向:

# 提高推理分辨率
results = model(img, imgsz=1280)

或使用更适合小目标的模型(yolo11x 或 RT-DETR)。

4. 类别冲突(person vs two_wheel)

人骑车时同时触发 person 框和 two_wheel 框,两框高度重叠。后处理规则:

def resolve_conflict(detections, iou_thresh=0.7):
    persons = [d for d in detections if d["class"] == "person"]
    bikes   = [d for d in detections if d["class"] == "two_wheel"]
    
    to_remove = set()
    for p in persons:
        for b in bikes:
            if compute_iou(p["bbox"], b["bbox"]) > iou_thresh:
                # 保留置信度高的
                if p["confidence"] < b["confidence"]:
                    to_remove.add(id(p))
                else:
                    to_remove.add(id(b))
    
    return [d for d in detections if id(d) not in to_remove]

工程状态推荐参数

参数实验值推荐生产值
conf_threshold0.250.45
iou_threshold0.450.55

后处理 Pipeline

results = model(img, conf=0.45, iou=0.55)
detections = parse_results(results)

# 1. 过滤极小框(去远处误检)
detections = [d for d in detections
              if d["bbox"]["width"] * d["bbox"]["height"] > min_area]

# 2. 类别冲突解决
detections = resolve_conflict(detections)

# 3. 业务逻辑处理

模型”能用”和”可上线”之间的差距主要就是以上这几个阈值和后处理步骤。