ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

自动标注流水线实战:Grounded-SAM + autodistill + X-AnyLabeling 搭建数据飞轮

自动标注流水线实战:Grounded-SAM + autodistill + X-AnyLabeling 搭建数据飞轮 1. 自动标注这件事为什么值得从头搭一遍做视觉项目的人都有一个共同的痛模型结构调得再花哨最后卡住进度的往往不是网络本身而是数据。尤其是目标检测、实例分割这类任务标注成本高得离谱。我手上一个工业质检的小项目两千张图三个人标了整整一周最后还因为标准不统一返工了三分之一。那段时间我就在想能不能让模型先帮我把框画个八九不离十人只做修正这就是自动标注的出发点。自动标注不是新概念但真正把它跑通、跑稳、跑成一条可持续的流水线中间要踩的坑比想象中多。标题里提到的三个东西——X-AnyLabeling、autodistill、Grounded-SAM——恰好代表了自动标注的三个关键环节交互式标注工具、知识蒸馏式自动打标框架、开放词汇检测分割模型。把它们串起来就能搭出一条从零标注到可用数据集的完整链路而且这条链路是可以滚起来的也就是大家常说的数据飞轮。这篇文章面向的是已经有一点深度学习基础、手上有实际标注需求的人。你可能是在做缺陷检测、遥感识别、零售货架分析或者只是想让自己的数据集扩充得快一点。不管哪种只要你能跑通 Python 环境、能看懂 YOLO 的标注格式这篇内容就能直接抄作业。我会把每个环节为什么这么选、参数怎么定、坑在哪里都掰开讲清楚。先说结论性的判断Grounded-SAM 负责从文字到框和掩码的冷启动autodistill 负责把大模型能力蒸馏到小模型的批量生产X-AnyLabeling 负责人机协同的精细修正与格式落地。三者不是替代关系而是流水线上的三道工序。理解了这个分工后面的操作就顺了。2. 三个核心组件到底各自解决什么问题2.1 Grounded-SAM用一句话把目标框出来Grounded-SAM 这个名字其实是两个模型的组合Grounded-DINO 负责开放词汇检测SAM 负责精细分割。Grounded-DINO 的特点是你可以用自然语言描述你要找的东西比如生锈的螺栓破损的包装袋它就能把对应的区域框出来不需要你事先定义类别、不需要训练。SAM 则接过这些框把框内的物体轮廓精确地抠出来得到像素级的掩码。这个组合的威力在于零样本。你不需要准备任何标注数据只要能用文字描述清楚目标它就能给你初步结果。对于冷启动阶段这意味着你可以用几分钟时间把一批完全没标过的图变成有框有掩码的候选标注。当然它的短板也很明显文字描述模糊时召回不稳定密集小目标容易漏类别多了之后 prompt 工程本身就成了体力活。2.2 autodistill把大模型的知识蒸馏成小模型autodistill 的思路很聪明。它不指望 Grounded-SAM 直接产出最终标注而是把它当成老师。流程是老师模型在一批无标注图片上生成伪标签然后用这些伪标签去训练一个学生模型比如 YOLOv8 或者 RT-DETR。学生模型训练好之后再拿它去批量推理剩下的图片速度快、成本低而且随着你不断修正和补充数据学生模型会越来越准。这就是数据飞轮的核心机制老师生成 → 学生训练 → 学生推理 → 人工修正 → 数据回流 → 学生再训练。每一轮循环学生模型都在变强人工介入的比例在下降。autodistill 把这条链路的代码封装得很轻基本上几十行就能跑通一个完整的蒸馏流程这也是我推荐它的主要原因——它不重不绑架你的技术栈。2.3 X-AnyLabeling人机协同的最后一公里模型给出的标注永远不可能 100% 正确尤其是边界、遮挡、小目标这些场景。X-AnyLabeling 的价值就在这里它是一个带 AI 辅助的标注工具支持 SAM、YOLO 等多种模型在线推理你点一下就能自动生成候选框或掩码然后手动微调。它还支持自动标注模式可以批量跑模型、批量保存结果再人工过一遍。更关键的是格式兼容。X-AnyLabeling 原生支持 COCO、YOLO、VOC、MOT 等主流格式的导入导出这意味着你从 Grounded-SAM 或 autodistill 拿到的结果可以无缝进到工具里修正修完再导出成训练框架认识的格式。这条链路打通之后标注这件事就从纯手工变成了审核 微调效率提升是数量级的。组件核心能力在流水线中的角色典型输入典型输出Grounded-SAM开放词汇检测 分割冷启动生成伪标签图片 文字 prompt框 掩码autodistill知识蒸馏训练批量生产与模型迭代无标注图片集训练好的学生模型X-AnyLabeling交互式标注 AI 辅助人工修正与格式落地模型预测结果标准格式数据集3. 环境搭建别在第一步就劝退3.1 硬件与基础环境的最低要求自动标注这条链路对硬件的要求分两段。Grounded-SAM 推理阶段吃显存SAM 的 ViT-H 版本大概需要 8GB 以上显存才能跑得舒服ViT-B 版本 4GB 也能凑合。autodistill 训练学生模型时YOLOv8n 这种小模型 6GB 显存足够如果要上 YOLOv8l 或者 RT-DETR建议 12GB 起步。X-AnyLabeling 本身很轻但如果你要在里面挂 SAM 做在线推理显存需求跟单独跑 SAM 差不多。我的建议是如果只有一张消费级显卡优先用 SAM ViT-B 或者 MobileSAM 做交互用 YOLOv8s 做学生模型。精度损失在可接受范围内但速度和显存占用友好太多。别一上来就追求最大模型跑通链路比堆参数重要。基础环境我习惯用 conda 隔离Python 版本锁在 3.10这个版本对 PyTorch、CUDA 以及几个标注库的兼容性最稳。CUDA 版本跟着 PyTorch 官方推荐走目前 11.8 和 12.1 都比较成熟。conda create -n autolabel python3.10 -y conda activate autolabel pip install torch torchvision --index-url https://download.pytorch.org/whl/cu1183.2 三个组件的安装顺序与依赖冲突处理这三个组件的依赖有重叠也有冲突安装顺序很关键。我的经验是先装 Grounded-SAM再装 autodistill最后装 X-AnyLabeling。原因是 Grounded-SAM 对 transformers、segment-anything 这些库的版本比较敏感先把它钉死后面两个相对宽容。Grounded-SAM 的安装核心是拉官方仓库、装依赖、下权重。权重文件比较大SAM 的 ViT-H 有 2.4GBGrounded-DINO 的权重也有几百 MB提前下好放到指定目录别等到跑的时候再下容易超时。git clone https://github.com/IDEA-Research/Grounded-Segment-Anything.git cd Grounded-Segment-Anything pip install -r requirements.txtautodistill 的安装就简单多了它本身是个框架具体用哪个老师模型、哪个学生模型再单独装对应的子包。比如用 Grounded-SAM 当老师、YOLOv8 当学生pip install autodistill autodistill-grounded-sam autodistill-yolov8X-AnyLabeling 我推荐直接下预编译版本省去 GUI 依赖的折腾。如果你非要跑源码注意它依赖 PyQt5在某些 Linux 发行版上需要额外装系统库。源码运行的话PyCharm 里配置好解释器直接跑anylabeling/app.py就行但记得把工作目录设对否则资源文件路径会出问题。注意三个组件都依赖 opencv-python但版本要求可能不同。如果出现cv2相关报错先检查是不是装了多个版本统一到一个较新的版本通常能解决大部分问题。3.3 权重下载与目录规划权重管理是个容易被忽视但很影响效率的事。我习惯在项目根目录建一个weights/文件夹把所有模型权重集中放然后在代码里用相对路径引用。这样换机器、备份、迁移都不会乱。需要准备的权重包括SAM 的 checkpointvit_b、vit_h 按需、Grounded-DINO 的 checkpoint、以及学生模型 YOLOv8 的预训练权重。SAM 和 Grounded-DINO 的权重在官方仓库的 README 里都有下载链接YOLOv8 的权重 autodistill 会自动下但手动下好放到缓存目录能避免重复下载。目录结构我一般这样组织project/ ├── weights/ │ ├── sam_vit_b.pth │ ├── groundingdino_swint.pth │ └── yolov8s.pt ├── data/ │ ├── raw/ # 原始无标注图片 │ ├── pseudo/ # 伪标签结果 │ └── dataset/ # 最终数据集 ├── configs/ └── scripts/这个结构不是强制的但清晰的目录规划能让你在调试时少花很多时间找文件。4. 用 Grounded-SAM 做冷启动从文字到第一批伪标签4.1 Prompt 设计的门道Grounded-SAM 的效果七成取决于 prompt 怎么写。这里有个反直觉的点不要用太泛的词也不要用太细的词。比如你要检测安全帽写helmet比写hat好因为 hat 会召回一堆无关的帽子但如果你写yellow safety helmet with logo又太细模型可能因为颜色或 logo 缺失而漏检。我的经验是采用类别名 关键属性的组合用句点分隔多个 prompt。Grounded-DINO 支持这种格式比如person . helmet . safety vest . machinery .注意每个 prompt 后面加空格和句点这是 Grounded-DINO 的格式要求不加的话召回会明显下降。另外box_threshold 和 text_threshold 这两个参数要调。box_threshold 控制框的置信度门槛默认 0.3 左右text_threshold 控制文字匹配的严格程度默认 0.25。密集小目标场景把 box_threshold 降到 0.2召回会好很多但误检也会增加需要人工修正来兜底。4.2 批量推理脚本的写法单张图跑通之后下一步是批量。我写了一个循环脚本遍历data/raw/下的所有图片对每张图跑 Grounded-SAM把结果保存成 COCO 格式的 JSON。这里有个细节不要每张图都重新加载模型模型加载一次要好几秒批量跑的时候必须把模型初始化放在循环外面。import os import cv2 import json from groundingdino.util.inference import load_model, load_image, predict from segment_anything import sam_model_registry, SamPredictor # 模型初始化放循环外 grounding_model load_model(configs/GroundingDINO_SwinT_OGC.py, weights/groundingdino_swint.pth) sam sam_model_registry[vit_b](checkpointweights/sam_vit_b.pth) sam.to(devicecuda) predictor SamPredictor(sam) TEXT_PROMPT person . helmet . safety vest . BOX_THRESHOLD 0.25 TEXT_THRESHOLD 0.25 results [] for img_name in os.listdir(data/raw): img_path os.path.join(data/raw, img_name) image_source, image load_image(img_path) boxes, logits, phrases predict( modelgrounding_model, imageimage, captionTEXT_PROMPT, box_thresholdBOX_THRESHOLD, text_thresholdTEXT_THRESHOLD ) # 用 SAM 生成掩码 predictor.set_image(image_source) # ... 后续处理这段代码的关键在于把模型加载和推理分离。实测下来批量处理一千张图模型加载只花一次时间整体速度能快 30% 以上。4.3 结果过滤与初步清洗Grounded-SAM 的原始输出会有一些明显不合理的框比如面积过小的、长宽比异常的、重叠度极高的。这些如果直接拿去训练会污染学生模型。我一般会做几层过滤面积过滤去掉面积小于图像面积 0.1% 的框这些基本是噪声。长宽比过滤去掉长宽比超过 10:1 的框除非你的目标本身就是细长条。NMS 去重对同类别的框做非极大值抑制IoU 阈值设 0.5 左右。过滤完之后把结果转成 COCO 格式的 JSON方便后续导入 X-AnyLabeling 修正。这一步的代码不复杂但能显著提升伪标签的可用率。我实测过不做过滤直接训练学生模型的 mAP 会比过滤后低 5 到 8 个点这个差距在后期很难补回来。5. autodistill 蒸馏让模型自己学会标注5.1 蒸馏流程的整体设计autodistill 的蒸馏流程可以概括为四步老师推理 → 伪标签生成 → 学生训练 → 学生推理。它的 API 设计得很简洁基本上定义一个老师、一个学生、一个数据集路径然后调train就行。但简洁不代表没有坑关键参数的选择直接决定蒸馏效果。老师模型我选 Grounded-SAM因为它在开放词汇场景下召回最好。学生模型我选 YOLOv8因为生态成熟、部署方便、推理速度快。这个组合适合大多数目标检测场景。如果你是做分割学生模型可以换成 YOLOv8-seg但要注意分割的蒸馏效果通常比检测差一些因为掩码的误差累积更明显。5.2 关键参数置信度阈值与训练轮次autodistill 里有两个参数最影响结果老师模型的置信度阈值和学生模型的训练轮次。置信度阈值决定了伪标签的质量和数量。阈值高伪标签少但准阈值低伪标签多但脏。我的经验是先用 0.3 左右跑一轮看看伪标签的数量和可视化效果再决定调高还是调低。如果目标是密集小目标可以降到 0.2如果误检明显提到 0.4。训练轮次方面学生模型不需要训太久因为伪标签本身有噪声训太久会过拟合到噪声上。YOLOv8s 在几千张伪标签上50 到 100 个 epoch 通常就够了。我一般设 80 个 epoch配合早停验证集 mAP 连续 10 轮不涨就停。from autodistill_grounded_sam import GroundedSAM from autodistill_yolov8 import YOLOv8 base_model GroundedSAM(ontologyCaptionOntology({ person: person, helmet: helmet, safety vest: safety vest })) target_model YOLOv8(yolov8s.pt) target_model.train(data/raw, epochs80, imgsz640)这段代码看起来简单但ontology的定义很关键。左边是 Grounded-SAM 认识的文字 prompt右边是你想要的类别名。这个映射关系要提前想清楚因为一旦学生模型训练完类别就固定了后面改类别要重新蒸馏。5.3 学生模型推理与数据回流学生模型训练好之后用它去推理剩下的无标注图片生成第二批伪标签。这批伪标签的质量通常比老师模型直接生成的要好因为学生模型已经学到了你数据集的分布特点。然后把这批伪标签导入 X-AnyLabeling人工过一遍修正错误导出成标准格式再回流到训练集里。这就是数据飞轮的完整闭环。每一轮循环学生模型都在变强人工修正的比例在下降。我做过一个对比第一轮蒸馏后人工修正率大概 40%第二轮降到 25%第三轮降到 15% 左右。到后面大部分图片只需要扫一眼确认不需要逐框调整。提示数据回流时要注意类别平衡。如果某一类在伪标签里特别少学生模型会偏向其他类。可以在回流时对稀有类做 oversampling或者在损失函数里加类别权重。6. X-AnyLabeling 实操人机协同的精细修正6.1 导入伪标签与界面配置X-AnyLabeling 支持直接导入 COCO JSON 或者 YOLO txt 格式的标注。导入之后所有框和掩码会显示在图上你可以逐个检查。界面左侧是文件列表右侧是标注列表中间是画布。快捷键方面A和D切换上一张/下一张W和S切换上一个/下一个标注Delete删除选中的标注这些高频操作建议早点形成肌肉记忆。如果你要在工具里挂 SAM 做在线推理需要在设置里配置模型路径。X-AnyLabeling 支持 SAM、SAM-HQ、YOLO 等多种模型配置好之后点一下目标就能自动生成掩码比手动画多边形快得多。这个功能对分割任务尤其有用我标一个复杂轮廓手动要一两分钟用 SAM 辅助只要几秒。6.2 修正策略哪些该改哪些可以放过人工修正不是要把每个框都调到完美那样效率就没了。我的策略是抓大放小明显漏检的补上明显误检的删掉边界偏差在 10% 以内的放过。因为训练时数据增强本身会引入扰动过于精细的标注对最终精度提升有限反而拖慢进度。具体来说我会优先处理这几类问题漏检的目标尤其是小目标和遮挡目标、类别标错的比如把安全帽标成头、严重重叠的框两个框几乎重合需要合并或删除。至于边界差几个像素、框稍微大一点小一点这些不影响训练直接过。6.3 导出格式与训练框架对接修正完之后导出格式要根据你的训练框架来定。YOLOv8 用 YOLO txt 格式每行是class_id x_center y_center width height坐标是归一化的。MMDetection 用 COCO JSON 或者 VOC XML。X-AnyLabeling 都支持导出时选对应的格式就行。这里有个容易踩的坑类别 ID 的映射关系。X-AnyLabeling 里的类别顺序可能跟你的训练配置不一致导出后要检查一下classes.txt或者 JSON 里的categories字段确保 ID 对得上。我遇到过好几次训练时类别全错位的情况排查半天才发现是导出时类别顺序变了。导出格式适用框架关键检查点YOLO txtYOLOv5/v8/v11classes.txt 顺序、坐标归一化COCO JSONMMDetection、Detectron2categories 的 id 与 name 映射VOC XML老版本框架filename 与图片实际路径一致MOT多目标跟踪帧号连续性、ID 唯一性7. 常见问题与排查技巧实录7.1 Grounded-SAM 召回不稳定怎么办召回不稳定通常有三个原因prompt 不合适、阈值设置不当、图片本身质量差。排查顺序是先看可视化结果再调 prompt最后调阈值。如果模型完全没框到目标多半是 prompt 用词跟模型训练分布不匹配换个更常见的英文词试试。如果框到了但漏了一部分降低 box_threshold。如果误检太多提高 text_threshold。还有一个容易被忽视的点图片分辨率。Grounded-DINO 对输入尺寸有要求太小的图会被放大太大的图会被缩小缩放过程中小目标可能就丢了。如果目标普遍很小建议先把图片切块分块推理再合并结果。7.2 autodistill 训练不收敛的排查思路学生模型训练不收敛先检查伪标签的质量。把伪标签可视化出来看如果框乱七八糟那问题在老师模型不在学生。如果伪标签看着还行但 loss 不降检查学习率、batch size 这些基础参数。YOLOv8 的默认学习率通常没问题但如果你的数据集特别小几百张学习率要调低否则容易震荡。另一个常见原因是类别不平衡。如果某一类的样本数只有其他类的十分之一模型会忽略这个类。解决办法是在伪标签生成阶段对稀有类降低阈值多召回一些或者在训练时用cls权重参数加大稀有类的损失权重。7.3 X-AnyLabeling 卡顿与崩溃的处理X-AnyLabeling 卡顿通常是两个原因图片太大、模型推理占显存。图片超过 4K 建议先缩放再标注或者用工具的切片功能。模型推理卡顿的话检查是不是同时挂了多个模型或者显存不够导致频繁换页。关掉不必要的模型或者换更小的模型版本。崩溃的话先看日志。X-AnyLabeling 的日志一般在用户目录下的.anylabeling文件夹里。常见崩溃原因包括标注文件格式错误、图片路径含中文或特殊字符、PyQt 版本不兼容。图片路径尽量用英文和数字别用中文和空格这个坑我踩过不止一次。7.4 数据飞轮转不起来的典型症状数据飞轮转不起来表现为每一轮人工修正量都很大学生模型精度上不去。这通常是因为回流数据没有经过清洗脏数据反复训练模型学到的都是错的。解决办法是每轮回流前做一次质量检查把明显错误的标注剔除别让它们进入训练集。另一个原因是类别定义不稳定。今天标安全帽明天标头盔模型就懵了。类别体系一旦定下来就不要轻易改。如果确实要加类重新蒸馏一轮别在旧模型上打补丁。问题现象可能原因排查动作解决方向召回低prompt 不匹配可视化检查换词、降阈值误检多阈值过低统计误检类别提阈值、加负样本训练不收敛伪标签脏可视化伪标签清洗数据、调学习率飞轮不转回流数据差抽查回流样本加质检环节工具卡顿图片过大看显存占用缩放、切片、换小模型8. 把这套流程跑成习惯之后的一些体会这套流程我前后在三个项目里跑过从工业质检到遥感识别场景不同但骨架是一样的。最大的感受是自动标注的价值不在于完全替代人工而在于把人工从画框变成审核。画框是体力活审核是判断活后者对标注员的要求更低速度也更快。一个熟练的标注员纯手工一天标 300 张顶天了用这套流程一天过 1000 张不是问题。另一个体会是别追求一步到位。第一轮蒸馏出来的学生模型肯定不完美但只要它能帮你省掉 50% 的工作量这轮就值了。后面每一轮都在这个基础上改进边际成本越来越低。我见过有人非要等模型精度到 90% 才肯用结果白白多标了几周。最后分享一个小技巧在 X-AnyLabeling 里给不同来源的标注设不同颜色。老师模型生成的用灰色学生模型生成的用蓝色人工修正过的用绿色。这样你一眼就能看出哪些是可信的、哪些需要重点检查审核效率能再提一截。这个功能在设置里的标注颜色选项里配花五分钟配好后面省的时间是几十倍。
返回列表