简介:面向工业视觉与AI工程应用人群的64页PDF文档,系统讲解YOLOv11在单阶段检测架构下,如何同时完成目标检测、图像分割与属性分析,并实现多任务融合部署。文档按目录模块化组织,覆盖YOLOv11基础原理、目标检测/分割/属性分析三大模块实现、多任务融合策略,以及智能制造、能源巡检、物流仓储等工业应用场景,内容完整、条理清晰。资源为单个PDF文件,压缩包大小约2.34MB,文档支持目录章节跳转,阅读器左侧大纲亦可快速定位;目前已有215人浏览学习。文档从骨干网络、检测头、损失函数、训练优化、推理后处理到案例拆解提供了系统讲解,既适合初学者建立YOLOv11多任务全景认知,也能帮助工程师在工业质检、设备巡检、农产品分级等场景中复用思路与方法。
1. 一台设备同时回答“是不是缺陷、区域在哪、属性是什么”
流水线摄像头拍下一块 PCB:板面有没有划痕?划痕区域在哪?是刻伤还是擦伤?过去这三件事要拆成三个模型、部署三套服务、管三份推理结果。YOLOv11 把三件事收拢到一个网络里,共享同一组特征,输出检测框、逐像素掩膜和属性分类向量。这套方案适合做产线质检、工件分拣或设备巡检的多目标场景,用一个模型替代两三个模型,近景、微观和划痕检测的落地成本会低不少。
2. YOLOv11 网络结构中三种输出头如何协同检测、分割与属性分析
2.1 共享主干不共享任务:一个 backbone 三个分支
YOLOv11 的分割版本延续了“backbone + neck + head”的整体框架:backbone 由 C3k2、SPPF 与 C2PSA 注意力模块堆叠,先做多尺度下采样,再对不同感受野特征做池化融合;neck 用 PAN-FPN 把高层语义和低层纹理交叉混合,最后输出 P3、P4、P5 三个尺度的特征图。检测、分割与属性分析三个分支从这个特征金字塔上取数,而不是各自单独跑一遍网络,这是“多任务融合”最省钱的部分——推理时只过一次主干,三个头共享计算。
对经济型设备来说,这个结构的价值在显存和延迟上都只涨一小截。工业现场常用的 640 分辨率下,多任务模型的主要增量来自三个分支的卷积头,主干推理成本基本不变,因此即便把分割和属性分析同时挂上,单张图的推理开销也比“检测模型 + 分割模型”串联低得多。这也是多任务融合在产线上真正能省算力的原因。
2.2 在检测头之外加一个 mask 头得到一个区域
分割分支采用“原型掩膜 + 掩膜系数”的机制:特征图经过多组上采样后生成若干张与输入比例对应的原型掩膜,检测头对每个候选框额外输出一组系数,最终把原型掩膜与系数做线性组合,再裁剪到检测框内,得到逐像素掩膜结果。它和 U-Net 那种整图逐像素分类的“语义分割”不同,天然挂在检测框之下,属于实例分割,支持同一个类别出现多个目标,并且每个目标可以独立统计面积、轮廓和尺寸。
工业场景里,我通常把分割分支的用法分成两档:只需要判断“有无缺陷”时,检测分支的置信度就够;需要输出缺陷轮廓、计算周长或判断边缘是否平滑时,才真正解析 mask 头。项目排期中如果预算有限,可以先在目标检测标注基础上补 5% 的掩膜样本做试探性训练,看 mask 分支的收敛情况再决定是否扩大标注范围,避免一上来就全量标注。
2.3 给目标打属性:在分类分支上做第二层标签
属性分析没有在官方 segment 模型里直接暴露一个现成头,常见做法是在检测分支的类别输出之外并联一个属性分类头。这个头输入目标框对应的特征向量,经过小卷积和全局池化后,输出 N 维属性向量。如果属性之间互斥(比如缺陷等级 A/B/C),接 softmax;如果多种属性可以共存(比如“边缘粗糙”和“表面氧化”同时成立),接 sigmoid 做多标签分类。
另一种省事方法是将属性直接编码进 class 类别,例如把“划痕-纵向”“划痕-横向”都拆成独立类别。这样不用改动网络,但类别数会指数膨胀,同类型不同属性的目标会互相竞争分类权值,长尾类别上表现很不稳定。两种方案对比后,加一个独立属性头通常只多出少量参数,却可以把“目标是什么”和“目标属性如何”彻底解耦,稳定性更好。
| 分支 | 输出形状 | 典型用途 |
|---|---|---|
| 检测分支 | [N, 4+nc] | 框坐标加类别得分 |
| 分割分支 | [N, H, W] | 逐像素掩膜,用于轮廓与面积 |
| 属性分支 | [N, attr_dim] | 缺陷等级、方向等属性向量 |
加载 YOLOv11n-seg 模型后,可以从返回对象中同时取到检测框与掩膜:
from ultralytics import YOLO
model = YOLO("yolo11n-seg.pt")
results = model.predict("pcb_001.jpg", conf=0.25, iou=0.5)
for r in results:
boxes = r.boxes.xyxy.cpu().numpy() # [N, 4] 检测框
labels = r.boxes.cls.cpu().numpy() # [N] 类别编号
scores = r.boxes.conf.cpu().numpy() # [N] 置信度
masks = r.masks.data.cpu().numpy() # [N, H, W] 掩膜
代码里 boxes 给出目标坐标, masks 是原图分辨率比例对应的布尔掩膜, labels 是检测与分割共用的目标 ID。属性头如果做成了独立模块, results 对象里通常不会有现成字段,可以在循环内按 boxes 坐标从特征图切片,或者直接把 labels 对应到外置属性标签表。关键点是 mask 分支输出的空间尺寸与原图缩放比例一致,坐标换算时要按 r.orig_shape 恢复原图比例。
2.4 模型 yaml 里的关键配置
在自定义训练时,模型结构一般不用手工改 yaml,而是修改数据集配置。下面是工业分割数据集的参考配置:
path: ./datasets/industrial_defect
train: images/train
val: images/val
names:
0: scratch
1: pit
2: solder_joint
names 的 id 顺序直接对应标注文件中的类别序号。属性头单独训练时,它的标签不写进这个 yaml,而是放在独立的属性标签表中。如果要在官方 yolo11n-seg.yaml 基础上加属性头,需要同步修改 Detect head 的 nc 和属性头的 attr_dim ,否则低层卷积形状无法对齐。
3. 先喂料再干活:构造“检测框 + 分割掩膜 + 属性标签”的工业训练集
3.1 标注方案与工具选择
多任务训练集需要三类信息:检测框、分割掩膜、目标属性。最容易落地的流程是“多边形标注 + 分类标签 + 附加属性字段”。单机处理数百张图时,用 Labelme 画 polygon 就能同时得到框和 mask,polygon 的外接矩形自动生成检测框,属性用 group_id 或额外字段记录。超过几千张图、多人并行时,建议切到 CVAT 这类带任务分派的项目化管理平台,标注人员只需要画多边形并选属性下拉项,导出 COCO 与自定义属性表。
人工标注并不等于所有样本都要从头画。工业产线的常见做法是:先用既有检测模型在正常样本上做粗筛,把误检、漏检样本反向补回标注队列,mask 标注只覆盖难例。对易分样本保留半自动标注结果,整体标注成本通常能压到全量标注的三分之一左右。
3.2 用脚本把标注结果转为 YOLOv11 格式
YOLOv11 的分割训练格式与纯检测不同,每张图的标签文件是与图同名的 txt,每行由“类别 id + 多组归一化 x,y 坐标”组成:
0 0.512 0.314 0.514 0.316 0.518 0.315 ...
1 0.203 0.411 0.210 0.418 0.212 0.412 ...
坐标必须归一化到 0~1,Labelme 转 YOLO 格式的脚本关键部分如下:
import json
from pathlib import Path
def labelme_to_yolo(labelme_json: str, output_txt: str):
with open(labelme_json, encoding="utf-8") as f:
data = json.load(f)
img_w = data["imageWidth"]
img_h = data["imageHeight"]
lines = []
for shape in data["shapes"]:
cls_id = class_name_to_id[shape["label"]]
points = shape["points"] # [[x, y], [x, y], ...]
norm_points = []
for x, y in points:
norm_points.append(f"{x / img_w:.6f} {y / img_h:.6f}")
lines.append(f"{cls_id} " + " ".join(norm_points))
Path(output_txt).write_text("\n".join(lines), encoding="utf-8")
这段脚本只处理 polygon 坐标的归一化,训练时 YOLOv11 会在内部根据这些多边形自动计算 mask 与检测框。如果标注里有 circle 或 rectangle 图元,需要先转成 polygon 点集,否则解析会报错。属性字段建议单独写一个与图片同名的属性文件,每行对应一个目标,按类别 id 顺序写入。
3.2.1 属性标签与图片的对应关系
属性标签不放进 YOLO 分割 txt,因为官方数据解析器只读 txt 里的类别与坐标。建议在数据集目录放一个 train_attr.json ,记录每张图里目标 id 与属性向量的对应关系:
[
{"image": "pcb_001.jpg", "attrs": [[0, 1, 0], [1, 0, 1]]}
]
attrs 的第 i 行对应第 i 个检测目标的属性标签,第二维长度与属性头输出维度一致,多标签属性用 0/1 表示。训练脚本加载图片时,先读 yolo 格式 txt,再按文件名索引这张属性表,即可把三个任务的数据对齐。
3.3 训练前同步增强:不能只翻图像
YOLOv11 自带的 mosaic、翻转和随机缩放都会同步处理检测框与 mask,但不会同步处理外置属性标签。如果属性是“缺陷宽度与高度的比值”,翻转不受影响;如果属性是“横向划痕/纵向划痕”,水平翻转后绝对方向已经变化,标签与图像内容就会错位。
解决办法有两种:一种是对属性做对称化编码,把“划痕在左/右”统一成“横向/纵向”;另一种是在自定义 Dataset 里把翻转操作与属性向量一起变换。产线项目里我通常直接关闭 Mosaic 和随机翻转,用固定背景贴片代替——多任务模型对位置抖动更敏感,尤其 mask 分支在翻转后,小目标边界容易出现错位。
3.3.1 增强时保持属性一致的最小改动
import albumentations as A
def get_transform(imgsz=640):
return A.Compose([
A.RandomResizedCrop(imgsz, imgsz, p=0.8),
A.HorizontalFlip(p=0.5),
A.RandomBrightnessContrast(p=0.3),
])
这套增强用 Albumentations 同时处理图像与检测框;如果需要同步处理 mask,需要额外传入 mask 参数。想保持属性一致,可以在 Compose 的返回值里取出翻转标记,再反转属性向量。比如水平翻转后,“左侧边缘粗糙”和“右侧边缘粗糙”两个互斥属性需要交换索引,否则训练时正负样本就是错的。
4. 训练多任务模型:损失权重、训练参数与调优策略
4.1 多任务损失怎么加权
多任务训练的核心不是网络设计,而是损失量纲的平衡。YOLOv11 官方训练脚本里,检测部分已经包含 box_loss 、 cls_loss 、 dfl_loss ,分割分支额外加 seg_loss 。属性头加入后,总损失形态一般是:
loss = w_box * box_loss + w_cls * cls_loss + w_dfl * dfl_loss
+ w_seg * seg_loss + w_attr * attr_loss
官方实现里 box 、 cls 、 dfl 的权重写在超参数列表中, seg 权重常见为 1.0。第一次训练多任务模型,我会把 w_attr 放在 0.5 到 1.0 之间;如果属性精度一直上不去,优先检查属性头输入特征是否出现梯度弥散,而不是一味增大权重。注意 cls_loss 与 attr_loss 不要挤在同一个分类头上,两者叠加在相同 logits 上会造成收敛振荡。在自定义 trainer 中追回属性损失的写法如下:
import torch.nn.functional as F
attr_loss = F.binary_cross_entropy_with_logits(
attr_logits, attr_targets.float()
)
total_loss = loss + 0.5 * attr_loss
这段代码把属性梯度只回传到属性头,不污染检测分类。若发现某个缺陷类别的 mask 误差大,说明 seg_loss 对难例惩罚不足,可以换成带 focal 系数的 Dice loss 变体,效果通常比调高基础权重更明显。
4.2 训练参数与启动命令
前置条件是已装好 ultralytics 与对应版本的 torch 环境。单卡训练的最简命令如下:
yolo train model=yolo11n-seg.pt data=industrial_seg.yaml \
epochs=150 imgsz=640 batch=16 device=0 \
optimizer=AdamW lr0=0.001 \
project=defect_multi
如果显存只有 8GB,需要先降低 batch; yolo 命令不会自动检测显存,训练中断在 profile 阶段通常是显存不足。常用参数的含义见下表:
| 参数 | 建议值 | 作用与调整方向 |
|---|---|---|
imgsz | 640 起 | 输入边长,显存紧张改 512;小目标多改 768/1024 |
batch | 16 | 24G 显存可尝试 24;不够则减半 |
epochs | 100~200 | 工业样本少时先跑 150,观察 mask 分支是否过拟合 |
patience | 30 | early stopping,也可设 0 强制跑完 |
mosaic | 0.5 | 首次跑多任务建议降低,避免小目标被过度缩小 |
fliplr | 0.0 | 属性具有方向性时必须关闭 |
model=yolo11n-seg.pt 加载的是官方预训练权重,新加入的属性头如果随机初始化,建议把属性头单独设一个更大的学习率,否则头部梯度收敛慢。常见做法是把属性相关层拆到独立参数组,或在超参中指定 attr_lr = 0.01 。
4.3 目标检测评价指标之外,还要同时看掩膜 IoU 与属性 F1
多任务模型不能只看检测 mAP,mask 分支可能收敛得很差而检测 mAP 仍然很高。我常用一段脚本在验证集上同时输出三组指标:
python evaluate_multi.py --model best.pt --data industrial_seg.yaml \
--metric det_mAP50,det_mAP50-95,seg_mAP50,seg_mIoU,attr_F1
没有现成脚本时,可先跑 yolo val 看检测与分割 mAP,再用属性验证集单独计算 attr_F1 。小批量工业数据中, seg_mIoU 比 seg_mAP50 更敏感,两者差值超过 10 个点时,优先排查 mask 分支是否漏掉了小目标区域。
属性 F1 要注意样本不平衡:划痕类缺陷往往占 90% 的样本,这类类别的 F1 虚高会掩盖低级缺陷的低召回。建议计算每个属性类别的 macro-F1 而不是 micro-F1,必要时给少数类样本在损失中乘反频率权重。
4.4 yolov11 小目标优化与长尾属性调优
工业缺陷里小目标占比非常高,针孔、毛发丝粗细的划痕在 640 分辨率下往往只有十几个像素。YOLOv11 训练默认把原图缩放到 640,小目标被进一步压缩,掩膜几乎丢失。我的调整顺序是:先调 imgsz=1024 ,再把 mosaic 降到 0.5,最后才考虑在 neck 中增加一层 P2 特征。
P2 特征图分辨率高,但显存和推理时间都会涨,对存量产线不是首选。更便宜的做法是 ROI 裁剪后单独放大再送网络,属性识别不依赖全局上下文时效果很好。阈值处理上, conf 从 0.25 降到 0.15 只能解决部分低置信度召回,如果小目标漏检集中在掩膜分支,应该观察 mask_threshold 而不是分类阈值。
5. 部署到产线:导出 OpenVINO、异步队列与结果回传
5.1 导出格式与输出一致性验证
模型训练后,先把权重导出为 OpenVINO IR 格式,工业现场常见 CPU 推理,这一格式比 ONNX Runtime 更省内存。导出命令:
yolo export model=defect_multi/weights/best.pt format=openvino imgsz=640 half=False
导出后用下面的代码验证输出是否一致:
model = YOLO("defect_multi/weights/best_openvino_model")
results = model.predict("test_012.jpg", conf=0.25)
results[0].save("test_012_out.jpg")
OpenVINO 导出后 results[0].masks 依然可用,如果属性头是自定义层,可能需要在后处理中单独取特征。遇到导出失败,优先排查 half=True 是否开启,低精度模式下 mask 分支边界往往出现锯齿;本地 PyTorch 推理比导出后高一个多点,通常是归一化层被折叠异常,不是网络结构问题。
5.2 用队列把相机帧和推理解耦
工业相机常见 30fps 甚至 60fps,单帧推理不可能跟上这个频率。常见做法是把相机帧放入有界队列,推理进程每次取一帧,实时率稳定在 10~15fps 即可;队列满时丢弃最老帧而不是阻塞采集线程。
import queue
frame_q = queue.Queue(maxsize=8)
def grab_loop(camera):
while True:
frame = camera.read()
if frame_q.full():
try:
frame_q.get_nowait()
except queue.Empty:
pass
frame_q.put_nowait(frame)
def infer_loop():
while True:
frame = frame_q.get()
result = model.predict(frame, imgsz=640)
publish_result(result)
注意 model.predict 每次内部都构建新 batch,频繁调用会重复分配内存。正式部署时复用同一个模型实例,需要保存推理结果的现场截图时,用 results[0].save(filename) 即可,画面会带上 mask 与检测框。
5.3 把结果交给下游 PLC:MQTT 回传
质检工位需要把“缺陷类型 + 位置 + 面积 + 时间戳”推给 PLC 或 MES,我常用 paho-mqtt 发布 JSON:
import paho.mqtt.publish as publish
payload = {
"defect": "scratch",
"bbox": [128, 256, 512, 768],
"area_px": 1234,
"attr": {"direction": "vertical"},
}
publish.single("factory/line01/defect", payload,
hostname="192.168.1.50", port=1883)
这套代码没有额外部署成本,下游订阅主题即可消费。属性头给出“纵向划痕”这类判断,面积由 mask 分支按像素累计,把 mask 转为 cv2.contourArea 能得到更稳定的物理面积。对要求“NG 立即停机”的产线,建议在队列里加入阻塞确认逻辑,只有 PLC 回 ACK 后才继续发送下一帧,避免异常品漏放。
459




被折叠的 条评论
为什么被折叠?



