ARTICLE DETAIL

资讯详情

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

半自动标注流水线:Grounded-SAM与autodistill三件套实战

半自动标注流水线:Grounded-SAM与autodistill三件套实战 最近接了个工业现场巡检项目要给几千张设备照片标注三类目标仪表盘、阀门、渗漏点。团队三个人手动标了三天一人一天三百张眼睛都快瞎了更麻烦的是三个人画的框风格还不一样有人框得紧有人框得松连类别都偶尔混。我意识到不能再纯手动搞了开始研究怎么把自动标注工具串成一条流水线。最后定下来的组合就是 X-AnyLabeling、autodistill 和 Grounded-SAM 三件套Grounded-SAM 负责从文本描述直接出分割掩码autodistill 负责把基础模型的标注能力转成目标模型的训练数据X-AnyLabeling 则作为人工复核和格式管理的枢纽。这套配置跑下来同样的数据量时间压缩到原来的三分之一左右而且标注一致性明显比纯手动好。下面把完整的选型逻辑、环境搭建、实操步骤和踩过的坑都写出来适合正在被数据标注折磨的目标检测/分割项目团队。1. 选型思考为什么是 X-AnyLabeling、autodistill、Grounded-SAM 三件套1.1 手动标注的瓶颈到底在哪里先用数据说事。目标检测场景里一张干净的单目标图片画个框大概十到二十秒但如果是拥挤场景比如工厂里一堆表计阀门互相遮挡一张图三分钟都未必标得干净。实例分割更夸张沿着边缘描一个精细的 mask慢的时候一张图要十分钟往上。按这个速度算五万张图的生产级数据集一个人全职要标大半年这还没算审核和返工的时间。更隐蔽的成本是质量漂移。标注员上午精神好框画得紧下午累了框随手拉大一圈新来的标注员对渗漏点理解不到位把水管接头的阴影也框进去。这种不一致进了训练集模型学到的边界就是错的。所以单纯堆人力解决不了问题关键是要有一条能产出初稿、再由人来兜底的流程把标注员从从零画框里解放出来只做判断和修正。1.2 三个工具各自的分工逻辑选择这三种工具有明确的分工考虑不是跟风凑数Grounded-SAM 负责从无到有。它在零样本的情况下只要给一句文本提示就能在图片里找出对应目标生成检测框和分割掩码。适合在没有任何标注数据时先产出预标注。autodistill 负责从有到练。它把 Grounding DINO、Grounded-SAM 这类基础模型封装成组件自动把所有图片转成 YOLO 格式的数据集再直接拉起 YOLOv8 等目标模型训练整个过程是教师-学生模式大模型当老师标数据小模型当学生学这份数据。X-AnyLabeling 负责质量兜底与格式枢纽。它有完整的图形界面能快速修正检测框、多边形和 mask还能导入导出 YOLO、COCO、VOC 三种主流格式。自动标注产出的东西最终都要进这里人工过一遍。简单说Grounded-SAM 出初稿autodistill 把初稿变成训练数据并驱动模型迭代X-AnyLabeling 做人工审核和格式转换。三者组合就是一条半自动标注流水线自动的部分解决效率人工的部分守住质量。工具/链路自动化程度人工依赖主要产出纯手动标注接近 0%非常高小规模高精度数据X-AnyLabeling 内置模型30% 到 50%高精标数据集Grounded-SAM 批量推理80% 左右中需抽检全图 mask 预标注autodistill 全流程90% 左右低训练前需审核可直接训练的数据集提示不要期待任何自动标注工具能完全替代人。训练集里哪怕只有 3% 的错误框模型表现都会明显抖动。自动化的目标是减少人工工作量不是把人工清零。2. 环境搭建与模型准备让我少走弯路的清单2.1 X-AnyLabeling 的获取与启动X-AnyLabeling 是 AnyLabeling 的增强分支界面基于 PyQt5内置了 SAM、YOLOv8、PaddleOCR 等一系列模型安装方式有两种。Windows 下最省事的办法是直接用官方 release 里的打包版解压后双击主程序就能打开如果你需要改源码或者要往工具里塞自定义模型建议用源码方式跑git clone https://github.com/CVHub520/X-AnyLabeling.git cd X-AnyLabeling conda create -n anylabel python3.9 conda activate anylabel pip install -r requirements.txt python main.pyLinux 下第一次跑大概率会遇到两个坑一是 PyQt 的 xcb 插件相关报错Ubuntu 下需要先执行sudo apt install libxcb-cursor0二是路径里有中文或空格会导致模型加载失败建议项目目录保持纯英文路径。装好之后每次启动只需要激活 conda 环境再执行python main.py看到主界面弹出就说明打开成功了。第一次打开如果界面卡顿多半是在后台准备内置模型权重属于正常现象模型权重默认放在项目内的 model 目录下载中断时手动把权重文件放进去、命名和配置文件保持一致即可。为什么用 conda 3.9 而不是最新版X-AnyLabeling 依赖的 PyQt 与 onnxruntime 在 3.7 和 3.11/3.12 上都出现过兼容性问题3.9 是最稳的实测很少因为 Python 版本本身报错。2.2 autodistill 的安装与依赖autodistill 安装本身不难麻烦的是它背后的 torch、CUDA 环境要事先就绪。建议在同一个 conda 环境里继续装pip install autodistill autodistill-grounding-dino autodistill-grounded-sam autodistill-yolov8如果你的显卡支持 CUDA提前把 PyTorch 装成 GPU 版本再装上面的包然后确认torch.cuda.is_available()返回 True。有个常见误区很多人先装 autodistill后补 torch结果 pip 把之前的 GPU 版覆盖成了 CPU 版Grounding DINO 推理全程走 CPU一张 640 分辨率的图要等好几秒批量跑几千张图完全不现实。所以我的固定顺序是先把 torch GPU 环境确认好再装 autodistill 系列包。权重文件同样要注意。第一次运行 autodistill 会自动下载 Grounding DINO 权重公司内网或网络不稳定的环境经常卡在下载阶段。我的做法是找一台网络通畅的机器把权重提前下好放到用户目录缓存或模型目录再运行脚本就不会卡。如果代码里指定了绝对路径多台机器协作时也不会出现找不着模型的问题。2.3 Grounded-SAM 独立部署与快速验证autodistill 里已经有 grounded-sam 的封装但我仍然单独部署了一份 Grounded-SAM。原因有两个一是官方 demo 的参数暴露更完整想调 box_threshold、text_threshold 这类细节时更方便二是 autodistill 底层跑的是固定流程出问题很难定位是模型问题还是脚本问题独立部署一份方便对照排查。部署步骤git clone https://github.com/IDEA-Research/Grounded-SAM.git cd Grounded-SAM pip install -r requirements.txt然后下载两个权重文件放到模型目录GroundingDINO 的groundingdino_swint_ogc.pth和 SAM 的sam_vit_h_4b8939.pth。SAM 的大模型权重接近 2.5GB8G 显存跑起来很吃力只是快速验证效果的话换sam_vit_b_01ec64.pth这类小权重更合适。跑一次官方 demopython grounding_sam_demo.py \ --config ../GroundingDINO/groundingdino/config/GroundingDINO_SwinT_OGC.py \ --grounded_checkpoint ./models/groundingdino_swint_ogc.pth \ --sam_checkpoint ./models/sam_vit_h_4b8939.pth \ --input_image demo.jpg \ --text_prompt cat \ --box_threshold 0.3 \ --text_threshold 0.25 \ --output_dir ./output能在 output 目录看到可视化结果就说明这一路是通的。后续做批量推理时把这段命令拆成 Python 循环脚本按产品需求逐张处理即可。3. X-AnyLabeling 实操人工标注与内建模型辅助3.1 界面结构与基本标注动作打开 X-AnyLabeling 后左侧是标签列表中间是图像画布右侧是属性面板底部有图像导航条整体布局和主流标注工具相近。第一次使用先把标签加好在左侧逐条添加类别比如 meter、valve、leak每个类别可以单独设定颜色。快捷键方面画矩形框按 R画多边形按 P切换到移动工具按 V保存按 CtrlS这些快捷键用顺后标注效率提升非常明显。画框的基本流程是打开一张图片按 R在目标上拉一个框选对类别然后下一张。如果目标有遮挡或者目标形状是弧形区域用多边形工具贴边描更靠谱。实例分割任务就不要用矩形框了直接在图像上描多边形导出时就是 polygon 格式。X-AnyLabeling 的撤销、复制标签、合并标签都在右键菜单里遇到重复目标时很省事。3.2 内置模型辅助标注的用法X-AnyLabeling 最实用的地方在于把模型推理放进了标注流程。右侧 AI 推理面板里选择要加载的模型比如 YOLOv8 或 SAM设置类别列表和置信度阈值点运行当前图片上就会自动出现预标注框或 mask。这些结果默认是待确认状态标注员只需要把 AI 画错的框删掉、漏掉的补上再修正一下框的贴合度比从零开始手动画快得多。实际用下来内置 SAM 辅助对分割任务的帮助尤其明显。在目标物体上点一个前景点再点一下背景点SAM 就会生成一个贴合边缘的 mask一些难处理的东西——比如弯曲管道上的渗漏点——看得准了很多。SAM 也不是万能的密集排列的同类物体会被合并成一个 mask这种时候要手动分割后再修正边缘。3.3 为什么人工兜底不可省我见过不少团队第一次跑自动标注看到 Grounding DINO 框得挺准就直接把标注结果扔进训练最后模型效果一塌糊涂。原因很简单自动标注的准是统计意义上的准单张图总会有漏检、误检、框偏移。而训练集里每张图都会被模型反复学习哪怕只有 5% 的错框也足够让模型的预测边界变得模糊。所以整套流程里我一直坚持一个原则自动标注结果必须经过 X-AnyLabeling 的人工复核复核通过后才能进训练集。复核不是一张张重新标而是把自动标注的初稿当作待修正稿人只关注明显的问题类别贴错、框没贴边、遮挡目标漏了、相邻目标被并框。熟练的标注员用这种方式处理一张复杂图大概只要三十到四十秒比完全手动快很多。这个机器初稿 人工修正的模式就是整套流水线的质量闸口。4. autodistill从基础模型到目标模型的自动闭环4.1 核心概念与执行逻辑autodistill 的设计思路一句话可以概括让一个大模型负责标注再让一个小模型学习这份标注。对应到代码里有两个核心角色一个是 Base model通常是体积大、零样本能力强的模型比如 Grounding DINO另一个是 Target model是真正要部署到业务里的轻量模型比如 YOLOv8n。两者之间通过 CaptionOntology 连接。CaptionOntology 本质上是算法最终要识别的类别名与喂给大模型的自然语言提示词之间的映射表。比如希望模型识别瓶子和穿反光背心的工人ontology 可以这样定义from autodistill.detection import CaptionOntology ontology CaptionOntology({ bottle: bottle, worker: person wearing reflective vest })左边是训练数据的最终类别名右边是给 Grounding DINO 的文字提示。基础模型拿文字提示去图片里找目标找到后生成检测框落盘成 YOLO 格式。4.2 一个可运行的标注与训练脚本完整脚本大概是这样from autodistill_grounding_dino import GroundingDINO from autodistill.detection import CaptionOntology from autodistill_yolov8 import YOLOv8 ontology CaptionOntology({ meter: pressure gauge, valve: industrial valve, leak: water leakage }) base_model GroundingDINO(ontology) dataset base_model.label( input_folder./raw_images, output_folder./auto_labeled ) print(dataset)跑完这一步去输出目录看正常情况下会看到按 train/valid 划分的数据集每个划分里都有 images 和 labels 两个子目录labels 里的 txt 文件与图片一一对应这就是一份可以直接丢给 YOLO 系模型训练的数据。具体目录层级会随 autodistill 版本略有差异跑完先ls看一下再写后续路径。接着训练目标模型target_model YOLOv8(yolov8n.pt) target_model.train( dataset, epochs50, imgsz640 )dataset 是上一步 label() 返回的对象里面记录了划分路径autodistill 会自己生成训练所需的配置文件不需要手工写 YAML。如果你的 autodistill 版本比较早train 的第一个参数直接传输出目录字符串也可以跑之前先看一眼当前版本的接口说明。4.3 提示词设计是自动标注质量的上限用 autodistill 过程中最深的体会是提示词写得好不好直接决定标注质量这部分没有任何参数能替你兜底。Grounding DINO 的文本提示对措辞非常敏感。我把提示词写成长句比如 a person walking on the road at noon漏检率明显上升把多个概念塞进一个提示词比如 person and dog模型会把两个目标当成一个整体框出来。现在的经验是每个类别只放一个清晰的名词短语场景词能去掉就去掉类别之间互相干扰的在 ontology 里换词或加细节来区分。提示提示词是自动标注质量的上限。先拿 20 到 30 张代表性图片跑一轮 label()肉眼检查预标注确认提示词对应目标都找得到再放开到全量数据。否则几千张图一次性标完回头发现一半框是错的重跑的时间成本很亏。5. Grounded-SAM 批量推理从文本提示到分割掩码5.1 Grounding DINO 出框SAM 出掩码Grounded-SAM 的组合原理说起来不复杂第一步Grounding DINO 读入文本提示和图像通过跨模态注意力机制找出与文本语义匹配的区域输出候选框和置信度第二步把候选框作为 prompt 传给 SAMSAM 在框内生成精细的分割掩码。这样就把文本到检测框和框到掩码接成了一条链等于用一句话做实例分割。为什么要绕这一步而不是直接用 SAM 自动分割所有区域因为 SAM 本身没有语义理解能力它知道哪里是物体但不知道哪个是仪表盘。Grounded-SAM 的文本提示补上了语义这一环让它只分割指定类别。这也是它能做零样本标注的原因不需要任何训练样本就能对陌生类别的图像输出掩码。5.2 官方 Demo 运行与参数含义前面第二章已经给了运行命令这里重点讲参数怎么调。box_threshold控制检测框的置信度阈值调低会让更多潜在目标被框出来但噪声也会增加text_threshold控制文本与视觉匹配的置信度阈值调低会显著提高召回率代价是误检变多。精度优先的场景两个阈值都设在 0.3 左右漏检严重的场景先把text_threshold降到 0.2观察输出再决定要不要继续降。输出目录里会有两类关键结果一类是可视化图片框和 mask 直接叠在原图上方便肉眼确认另一类是包含掩码信息的 JSON 文件里面用 COCO 风格的标注格式记录了每个目标的位置和 mask 像素位置这份 JSON 是后续转成 YOLO 格式或者导入 X-AnyLabeling 复核的重要中间文件。5.3 批处理的显存与效率经验批量推理时最容易翻车的是显存。SAM 的 ViT-H 权重非常吃显存8G 显卡处理 1280 分辨率的图几张就会出现 CUDA out of memory。我的处理办法有两招第一把输入图片先缩放到宽边 1280 以内再推理很多业务场景的精度损失可以接受第二在循环脚本里每处理完一张图就释放中间变量必要时加torch.cuda.empty_cache()。如果依然溢出把 SAM 权重换成 ViT-B 版本速度提升明显掩码质量在大多数场景下够用。另一个隐性问题是小目标。目标在整张图里占比很小时Grounding DINO 容易漏。解决思路是切图推理把大图切成若干有重叠的 tile分别推理后再把结果合并回原图坐标。代价是推理时间变长我一般在主要类别确实以中小目标为主时才启用。6. 三条标注数据的汇合格式转换与复标闭环6.1 工具之间的格式差异三种工具或脚本产出的数据格式各不相同这是整个流程里最繁琐也最容易出错的一环先把常用格式理清楚数据来源输出格式特点X-AnyLabelingYOLO txt / COCO json / VOC xml也支持内部 workdir导入导出灵活类别顺序由标签列表决定autodistillYOLO txt按 train/valid 划分每行class x_center y_center width height数值归一化到 0~1Grounded-SAM 官方 demoCOCO json mask png掩码以 polygon/segmentation 形式记录最容易出问题的点在于坐标换算。COCO 的 bbox 是绝对像素坐标的[left, top, width, height]YOLO txt 是归一化的[x_center, y_center, width, height]。很多人直接把 COCO 的 left/top 当 YOLO 的 x_center/y_center 用结果训练时所有框都偏到图像角落。注意每次转换后抽几张图把框画回原图上确认一遍。坐标问题用肉眼检查十分钟比训练到一半才发现错误省事得多。6.2 COCO JSON 转 YOLO TXT 的核心脚本下面是我项目里一直在用的转换脚本只针对检测框场景分割 mask 的转换逻辑类似区别是多一步把 polygon 转成归一化坐标import json from pathlib import Path def coco_to_yolo(coco_path, image_dir, output_dir): output_dir Path(output_dir) output_dir.mkdir(parentsTrue, exist_okTrue) with open(coco_path, r, encodingutf-8) as f: data json.load(f) image_id_to_name {img[id]: img[file_name] for img in data[images]} image_id_to_size {img[id]: (img[width], img[height]) for img in data[images]} cat_id_to_label {cat[id]: i for i, cat in enumerate(data[categories])} for ann in data[annotations]: img_name image_id_to_name[ann[image_id]] width, height image_id_to_size[ann[image_id]] cat_id ann[category_id] x, y, w, h ann[bbox] x_center (x w / 2) / width y_center (y h / 2) / height norm_w w / width norm_h h / height label_path output_dir / (Path(img_name).stem .txt) with open(label_path, a, encodingutf-8) as f: f.write(f{cat_id_to_label[cat_id]} {x_center:.6f} {y_center:.6f} {norm_w:.6f} {norm_h:.6f}\n)脚本里继承了原图的宽高信息每个标注写的都是归一化之后的坐标。类别 id 的映射顺序需要固定下来训练时的类别顺序和这里保持一致否则模型输出的类别就全乱了。6.3 从自动初稿到人工复标再回训练的闭环我现在跑项目的标准闭环是这样第一批图片先用 autodistill 或 Grounded-SAM 自动标注产出预标注数据把这些预标注统一转成 X-AnyLabeling 能打开的格式标注员在界面上逐类复核修正复核通过后的数据重新导成 YOLO 格式拿去训练目标模型目标模型训练好后把它对剩余海量图片的推理结果再一次作为预标注交给人复核形成自动初稿 → 人工修正 → 模型迭代 → 再自动初稿的循环。这个循环里最值钱的部分是模型精度越高人工修正工作量越小。第一轮可能一张图要改三十秒跑过两轮之后很多图只需要翻一下就能确认单张耗时降到十秒以内。最终目标不是把人工降到零而是让人工专注处理模型搞不定的硬样本这才是自动标注流程的核心价值。7. 踩坑实录模型加载、显存与提示词的七个大坑7.1 环境与启动类第一个坑是 X-AnyLabeling 在 Linux 上报 xcb 相关错误。当时我以为是 PyQt5 装坏了反复重建环境最后查出来是缺libxcb-cursor0系统库执行sudo apt install libxcb-cursor0后问题消失。遇到类似报错先搜系统库缺失不要一上来就怀疑 Python 包。第二个坑是 onnxruntime GPU 版本和 CUDA 版本不匹配导致 X-AnyLabeling 内置模型始终在 CPU 上推理图一多就卡成幻灯片。解决方法是先卸载 onnxruntime按自己 CUDA 版本装对应版本的onnxruntime-gpu再在模型推理面板里确认设备已变成 GPU。7.2 推理与显存类第三个坑是自动下载权重中断。Grounding DINO 的权重文件比较大网络代理环境下经常下到一半失败。我的处理方式是提前手动下载好权重放到代码指定路径并把模型路径写成绝对路径避免不同机器上相对路径解析不一致。第四个坑是批量推理显存溢出。前面讲过优先降输入分辨率、换 SAM 小权重、逐张释放。另外一个容易被忽略的点不要同时开多个进程跑 Grounded-SAM每个进程都会把模型完整加载进显存四个进程就能把 24G 卡吃满。要加速就增大单进程内部 batch而不是开多个进程。7.3 提示词与数据类第五个坑是提示词写得太文学。想标无人机写 a drone flying in the sky检测率和只写 drone 相比有明显下降。我现在统一用最短的类别名词容易出现误检的近义类别在 ontology 里做区分比如 meter 和 gauge 同时使用时给不同文本提示并搭配样本检查。第六个坑是类别中文名问题。训练环节对中文类别名支持不好而 X-AnyLabeling 导出的标签列表可能是中文。我吃过一次亏训练中途报错才发现 classes.txt 里全是中文。现在的做法是工具里就用英文类别名展示层再映射成中文或者转换脚本里统一做映射。第七个坑是重叠框。自动标注结果经常出现两个模型对同一个目标各输出一个框或者同一目标被相邻提示词重复召回。让标注员手工删太累我加了后处理步骤对整张图跑一次 NMS把 IoU 大于 0.5 的重复框合并只保留置信度高的那一个。Grounded-SAM 的 JSON 输出可以在转换脚本里顺手做 NMSautodistill 的结果要看版本老版本一般没做需要在进入训练前自己过滤。最后说一点个人体会。这套自动标注组合真正适合的场景是数据量中等到大、类别相对固定、目标形态不是特别极端的项目。它不会让标注这件事消失而是把人的工作从画框描边挪到判断与修正。我实际跑下来最大的收获不是省了多少小时而是标注质量稳定了——机器初稿的框虽然不完美但至少不会像不同标注员那样风格漂移。如果你也在为标注效率发愁我的建议是别一上来全量自动化先拿三五十张图把每个环节的格式打通确认提示词和阈值靠谱再慢慢放开规模。数据管道这种事越早理顺后面越省心。
返回列表