做数据标注这行最烦的就是重复劳动。一个框一个框去点、去拖、去调整顶点,几千张图下来眼睛都快瞎了。手速再快也顶不住数据量增长,而且不同人标注的尺度还不一样,同一个人上午和下午的标准都能飘。我这一年多一直在折腾自动标注这条路,从半自动到全自动,从单模型到多模型配合,最后稳定用下来的组合就是 X-AnyLabeling 加 autodistill 加 Grounded-SAM 这三件套。这篇就把整套流程拆开讲透,从原理到实操,包括我踩过的坑,一次性都交代了。
先说清楚这套东西能解决什么问题。简单讲就是:用 Grounded-SAM 这种大模型当“老师”,把没标注的图片自动识别出目标并生成精细的掩码;用 autodistill 把这套“老师”逻辑封装成流水线,批量处理、格式转换一把梭;最后用 X-AnyLabeling 当“人工质检台”,把自动标注的结果拉进来人工审核修正。三个工具各管一段,组合起来就是一条从原始图片到可用训练集的完整流水线。不管是做目标检测、实例分割还是语义分割的数据集,这套流程都能用,尤其适合那种数据量几千上万、类别固定、标注规范明确的场景。
1. 整体设计思路:为什么是这三件套
1.1 手动标注的痛点与自动化的核心逻辑
先聊聊数据标注这个环节在整个项目里的位置。CV 项目的模型训练、调参、部署,每一样都有成熟的工具链和教程,唯独数据标注这件事,听起来简单,做起来磨人。我最早用 LabelImg,后来换 labelme,体验就是效率天花板太明显了。一张图如果有五六个目标,光拖框和抠多边形就要一两分钟,遇到边缘复杂的物体更是折磨,船翻了都要重新来。整个项目几百G的数据,光标注就占掉三四周,训练本身反而只要一两天。
当时我就在想,有没有一种方式能把这种重复劳动压缩掉 80% 以上。后来慢慢摸索出来一条思路:先把自动标注模型跑出结果,人工只做审核和修正。这个逻辑听起来简单,但真正落地要解决三个问题:自动标注的模型从哪来、怎么批量跑、结果怎么交给人工快速修正。这三个问题刚好对应 Grounded-SAM、autodistill、X-AnyLabeling 三个工具。
这个思路本质上是一种“人机协作”的工作流。大模型负责“尽力而为”地生成标注雏形,人的精力从“逐像素绘制”转变为“审核和微调”,效率提升非常明显。我们实测同一个项目,纯手工一个人一天最多 200-300 张,人机协作可以干到 1500-2000 张,而且修正工作比从零开始画要省力得多,因为框的位置和掩码的轮廓已经八九不离十了。
1.2 三个工具在流水线中的定位分工
Grounded-SAM是整个流程里的“智能标注员”。它把两个模型串联在一起:用 Grounding DINO 做开放集检测,再让 SAM 为检测结果生成像素级分割掩码。也就是说,你告诉它“请找到图像中的水杯、鼠标、显示器”,它就能把每个目标的位置框出来,并且把目标轮廓的精细掩码也一并生成。
autodistill是“流水线管理员”。它本身就是一个自动化标注框架,把 Grounded-SAM(或者其他基础模型)封装成标准化的接口,同时提供数据集的存储、格式转换、模型训练的完整流程。简单说,autodistill 让“调用 Grounded-SAM 批量标注 1000 张图片”这件事变成了一段代码能搞定的事,而不是要自己写一堆 IO 和逻辑。
X-AnyLabeling是“人工质检员”的操作台。它是一个带 GUI 的开源标注工具,特别关键的一点是它支持导入多种格式的标注文件,并且直接内嵌了 AI 自动标注能力。我们用它来打开 autodistill 生成的标注结果,人工在界面上检查、修正、确认,最后导出成目标格式。这个工具让“人工审核”这个环节变得非常高效,因为它不是让你重新标注,而是让你在已有结果上做微调。
这三个工具的分工可以类比成一条生产线:Grounded-SAM 是产线上的机械臂(干活),autodistill 是调度系统(排产),X-AnyLabeling 是质检台的屏幕和工具(人工把关)。任何一个环节缺失,流程都会变得割裂。只上自动标注,出来的结果有人工复核会很乱误标漏标;只上人工标注,效率又回到底线的状态;只靠脚本硬凑,格式转换和异常处理就是新的大坑。
1.3 适用场景与边界判断
这套流程不是万能药,我自己试下来比较适合的场景有三个特征:
第一,图像中的目标物是常见类别,比如人、车、动物、日常用品这类。因为 Grounded-SAM 的效果跟文本检测基座的能力正相关,如果是极度小众的物体,比如某种特殊的工业零件、罕见昆虫亚种,效果就会明显下滑,需要人工修正的量陡增,反而得不偿失。
第二,标注规范明确且相对统一。比如“人都要标注”“车辆要标注到车身钣金边缘”这种规则,模型不需要理解规则,但人工修正时有明确的判断依据,流程更顺畅。
第三,数据量中等偏大(几千张以上)且时间紧张。几百张的量手动标注也就一两天的活,搭建整套环境反而费时费力。自动标注的真正价值在于规模效应,量越大,省的时间越明显。
我后来还把这套流程用在一个“掌心常见小物件”数据集上,大约 4000 张图片,目标类别 12 种。第一轮自动标注的初始置信度高、掩码质量也好的大约有 7 成左右,剩下 3 成需要人工修正。即便这样,整个项目的标注周期也从预估的 9 天压到了 2 天半。对于这种“量大、类别常规”的典型场景,这套自动标注流程的投入产出比很高。
2. 环境准备与工具选型
2.1 硬件与基础环境配置
先说硬件底线,Deep Learning 那个方向避不开的是显存。Grounded-SAM 这条链路的显存占用大头主要在 SAM 模型,我们用默认配置跑 1080p 图片的时候,显存峰值大概在 6-8GB 之间。如果图片分辨率再高,或者一次处理批次过大,可能会冲到 10GB 以上。所以一张 8GB 显存的显卡是起步线,16GB 会更从容。CPU 和内存的影响相对小,但标注大量数据时 CPU 会被图像解码和预处理的活儿顶起来,建议内存至少 16GB。
系统方面,我在 Ubuntu 20.04 和 Windows 11 上都跑过。Ubuntu 上环境部署更顺滑,依赖冲突少,推荐作为主力。Windows 上其实也能跑,只是有些 Python 包需要额外处理 MSVC 编译环境,比较费神。如果你只是用 X-AnyLabeling 的人工审核功能,它是完全跨平台的,Windows 上用起来反而最顺手。
Python 版本建议锁定 3.10,别用 3.11 或更高,某些依赖(比如特定版本的 PyTorch、Grounding DINO 相关的编译包)在 3.10 上最稳。我用 conda 建独立环境,避免和别的小项目互相污染。
2.2 X-AnyLabeling 安装要点
X-AnyLabeling 安装方式挺亲民的,GitHub 上直接拉源码就行。
git clone https://github.com/vietanhdev/anylabeling.git cd anylabeling pip install -r requirements.txt python anylabeling/app.py拉源码后第一次启动会在后台下载几个默认模型权重,网络状态不好的话这一步容易卡住,甚至直接闪退。解决办法是手动把模型文件下载好,放到它指定的~/.anylabeling目录下面,具体模型文件可以看它 GitHub README 里的链接。如果你在国内网络环境,可能要手动处理一下下载代理的问题,或者找一个网速稳定的时段来拉权重。这个工具本身界面逻辑很简单,左上角选模型,画布上拖拽标注,快捷键 W 画框、E 画多边形、Q 选择/移动,中键拖动平移,滚轮缩放。人工审核自动标注结果主要就是这几个键,熟练之后操作速度非常快。
有一点特别提一下,X-AnyLabeling 默认模型列表里就有 SAM 系列(包括 SAM ViT-B、MobileSAM 等),这意味着你甚至可以在 GUI 里直接对单张图做自动标注,不需要走 autodistill 那套批量逻辑。我把 X-AnyLabeling 的“AI 辅助”定位成“单图打补丁”模式,特别适合那种批量标注完之后的抽查与修正环节。
2.3 autodistill 安装要点
autodistill 是 Roboflow 出的开源框架,安装非常简单:
pip install autodistill autodistill-grounded-sam注意要配合一个 Grounded-SAM 的封装包。核心 API 就三行:
from autodistill_grounded_sam import GroundedSAM from autodistill.detection import DetectionOntology from autodistill.utils import plot base_model = GroundedSAM( ontology=DetectionOntology.from_string("person. car. dog."), box_threshold=0.35, text_threshold=0.25 )这里的ontology就是类别清单,模型会根据这里写的文本去识别目标。注意一个细节:如果你不设置OPENAI_API_KEY,这个库会直接用你输入的文本去跑 Grounding DINO;如果设置了,它还会用 Llama/OpenAI 对 prompt 做一轮扩展,生成更丰富的描述词来提升召回率。我实际测试下来,设置 API key 之后对低置信度区域的召回确实有提升,但如果你不方便配置外部 API 调用,不设置也能跑,只是要接受模型按字面词检索的局限性。
2.4 Grounded-SAM 模型部署细节
Grounded-SAM 本身不是一个 pip 包,而是一个 GitHub 项目,需要拉代码和权重文件:
git clone https://github.com/IDEA-Research/Grounded-SAM.git cd Grounded-SAM pip install -r requirements.txt权重文件有两个:Grounding DINO 的(一般用groundingdino_swint_ogc.pth,大约 700MB)和 SAM 的(ViT-B 大约 375MB,ViT-H 大约 2.5GB)。默认代码会假设权重文件放在指定路径,我建议把两个权重统一放到./weights/目录下,然后在代码里显式拼接路径。实际跑推理时,可以先跑一个小数据集做验证,确认检测结果符合预期之后,再全量跑。
显存优化方面,SAM 的model_type有vit_b、vit_l、vit_h三档。我的经验是:初始自动标注用 vit_h 效果最好,但如果你显存只有 8G,强烈建议用 vit_b 或 vit_l。vit_h 的掩码边缘精度确实高,但 1080p 图片单张推理时间大约是 vit_b 的 3-4 倍,在大批量场景下这个时间成本非常高。如果你对掩码边缘质量不是极致要求,或者后续还要人工修正,vit_b 完全够用。初期选型如果拿不准,用 vit_b 试跑一批看看效果再决定。
3. 核心细节解析:每个环节的关键决策与逻辑
3.1 Grounded-SAM 的文本检测链路与提示词设计
Grounded-SAM 的原理值得展开讲一下,因为它直接决定了我们后面怎么调参数。它由两段模型串联:
- Grounding DINO:一个开放集目标检测模型,输入文本,输出目标框。它把文本编码器输出的特征与图像特征做跨模态匹配,所以它能检测“训练时没见过”的类别,只要文本提示词描述得足够准确。
- SAM:拿到 Grounding DINO 输出的目标框作为“提示”,在框内分割出像素级掩码。SAM 的训练目标就是“给定任何分割提示,输出高质量掩码”,所以它对边缘细节的处理能力很强。
两段模型默认独立运行,所以提示词的设计非常关键。我举一个实际例子:我在一个“工地安全装备检测”项目里,直接写 “helmet” 时,漏检率很高,因为模型把安全帽跟普通帽子、兜帽混淆了。把提示词改成 “yellow hard hat on head of a worker” 之后,准确率明显上升。提示词不是越长越好,但针对性一定要强,把目标的场景属性、位置关系、视觉特征都描述进来效果最好。这种提示词的写作逻辑跟我调试开源模型 prompt 的套路是相通的——语义上要跟模型训练语料里常见的视觉表达对齐。
调试过程中常用box_threshold和text_threshold这两个阈值。box_threshold控制检测框置信度,调低一点(0.3 以下)可以提升召回,但会引入较多误检;调高一点(0.5 以上)更保守,适合类别容易混淆的场景。text_threshold控制文本与视觉特征匹配的置信度,我一般保持在 0.25-0.3 之间。这两组参数可以直接通过 autodistill 的GroundedSAM传给底层模型,也可以在官方推理脚本里调整。
3.2 autodistill 的数据流与蒸馏式标注逻辑
autodistill 的设计思路可以理解成“大模型打标签,小模型学本领”。它天然拆分出两个模型:基础模型(teacher)负责生成标注,目标模型(student)最终要在数据集上训练。整个流程是:
- 指定一个包含图片的文件夹
- 让基础模型批量推理,生成标注
- 将标注结果与图片一起封装成数据集对象
- 用这个数据集训练目标模型(比如 YOLOv8)
- 训练完成后,后续推理就不再需要基础模型了,直接用轻量目标模型
这其实是一种知识蒸馏思路的简化落地版,它不是让大模型直接喂给小模型特征,而是把大模型生成的标注当成标签,再用小模型去拟合。这个设计很贴合工业界的用法:部署环境跑不动大模型,但训练数据可以由大模型批量生产。
autodistill 的Dataset对象支持多种格式输出,包括 COCO、YOLO、TFRecord 等。我一般习惯先用dataset.export()导出成 COCO,再转成 X-AnyLabeling 能打开的格式,这样整条流程的衔接最顺。具体转换代码在后面章节会给到。
3.3 X-AnyLabeling 的格式兼容性与人工审核效率
X-AnyLabeling 是我用下来最顺手的标注工具,主要原因在于它支持多种标注格式的读取和保存,包含 LabelMe JSON、COCO、YOLO txt、VOC XML 等。更贴心的是,它可以读取已有的 LabelMe JSON,并将 SAM 的标注结果以 JSON 的形式直接叠加到图像上,这意味着自动标注的结果几乎无缝衔接人工审核。
标注文件的导入导出一个微型工作量,如果格式不兼容,整条流水线的价值就大打折扣。X-AnyLabeling 在“格式兼容性”这个维度做得非常到位,它甚至支持 YOLOv8 分割格式,也就是class_id x1 y1 x2 y2 ... xN yN这种变长格式。我实际测试过,把 autodistill 导出的 COCO 转成它的可读格式之后,打开、修正、再导出,整个过程流畅稳定,没有出现过文件损坏或坐标偏移的问题。
另一个提升效率的点是内置的交互式分割工具。当自动标注结果不理想,需要“抠”出某些轮廓时,我可以用它内置的 SAM 交互模型,在目标区域点几下,就能快速生成新的掩码,比手工用多边形慢慢描快得多。这正好跟自动标注形成互补:自动标注负责“铺量”,人工审核负责“精修”,精修过程中再借助 AI 能力提升效率。
4. 实操过程:从原始图片到可训练数据集
4.1 阶段一:用 autodistill + Grounded-SAM 批量生成初始标注
这一步是整个流程中最关键也最高产的部分。我拿“工地安全装备检测”项目来走一遍完整流程。
先准备数据,把未标注的图片统一放入一个文件夹,比如./images/raw。接着创建一个 Python 脚本,内容大概这样:
from autodistill_grounded_sam import GroundedSAM from autodistill.detection import DetectionOntology ontology = DetectionOntology.from_string( "person. hard hat. safety vest. car. truck. excavator." ) base_model = GroundedSAM( ontology=ontology, box_threshold=0.35, text_threshold=0.25 ) dataset = base_model.label( input_folder="./images/raw", output_folder="./datasets/labeled", output_format="coco" )执行之后,./datasets/labeled下就会生成 COCO JSON 格式的标注文件和对应的图片。整个批处理过程会自动遍历目标文件夹下的所有图片,自动生成标注。这里有个关键点:output_format="coco"生成的是单文件 COCO JSON,如果你之后要在 X-AnyLabeling 里逐张打开修正,可能需要再拆成单张的 LabelMe JSON。我用一个简短脚本做了这个拆分。
跑批处理的时候,我建议先拿 50 张图试跑,把box_threshold和text_threshold调好,确认召回率和误检情况在可接受范围,再全量跑。如果不调试直接全量跑,很可能某个提示词设计不当导致大量图片的标注质量偏低,返工成本比手工标注还高。
4.2 阶段二:COCO JSON 转 X-AnyLabeling 可读的标签文件
X-AnyLabeling 原生支持 LabelMe JSON 格式,也就是每张图对应一个.json文件,结构大致是:
{ "version": "5.2.1", "flags": {}, "shape": [], "imagePath": "img001.jpg", "imageData": null }autodistill/COCO 输出是集中式的,所有标注都集中在annotations数组里。我要把它拆成图片维度的shapes。核心转换逻辑大致是这样:
def coco_to_labelme(coco_json_path, output_dir): with open(coco_json_path, "r") as f: coco = json.load(f) images = {img["id"]: img for img in coco["images"]} categories = {cat["id"]: cat["name"] for cat in coco["categories"]} for img_id, img in images.items(): labelme_json = { "version": "5.2.1", "flags": {}, "shapes": [], "imagePath": img["file_name"], "imageData": None } for ann in coco["annotations"]: if ann["image_id"] != img_id: continue category_name = categories[ann["category_id"]] if "segmentation" in ann and ann["segmentation"]: # 使用分割掩码 shapes.append({ "label": category_name, "points": 展开多边形坐标, "shape_type": "polygon", "flags": {} }) elif "bbox" in ann: # 使用检测框转为四点多边形 ... with open(os.path.join(output_dir, img["file_name"] + ".json"), "w") as f: json.dump(labelme_json, f)展开多边形坐标时要注意坐标系的对应关系。COCO 的 segmentation 坐标是绝对像素坐标,而 X-AnyLabeling 的points也是绝对坐标,两者直接匹配,不用归一化。如果自动检测回来的是检测框而不是多边形,我会把矩形框扩展成四边形的四个顶点,在 X-AnyLabeling 里再手动细化边缘,这样至少位置和大小是对的。
这个转换脚本属于典型的“一次性工具”,写完跑完就可以扔,但代码本身值得好好写,因为有大量边界情况需要处理,例如空标注的图、segmentation 中带空洞的多边形环、多个标注类别的顺序问题等。
4.3 阶段三:X-AnyLabeling 人工审核与修正
把拆好的 JSON 放进 X-AnyLabeling,打开对应的图片就能看到自动标注的结果。我的工作流程是这样的:
第一遍快速浏览,只关注有没有明显的大错误,比如完全没有检测出来的目标、检测出的框位置差太远、两个目标被切成同一个掩码等。这类问题直接删掉错误标注或重新用内置 SAM 交互分割重做。
第二遍做细修,重点是掩码的边界贴合度。建筑工地场景中,人穿着反光背心、戴着安全帽,SAM 生成的掩码通常具有良好的质量,但在遮挡和模糊边界处会有瑕疵,比如安全帽的掩码把头发也圈进去了。这时候用 X-AnyLabeling 的多边形编辑工具微调几个顶点就行,一张图平均耗时不到 30 秒。熟悉了快捷键之后效率更高,我按 Q 切换到选择工具,直接拖拽顶点调整。
这个环节的核心心态是“只修错误,不要过度完美”。自动标注的目标是生成 80 分以上的标签,人工把它提升到 95 分以上即可。如果追求每个掩码都是完美的像素级贴合,那反而违背了自动标注提效的初衷。
4.4 阶段四:导出数据并训练目标模型
审核完成之后,X-AnyLabeling 可以一键导出 YOLO 格式、COCO 格式或者 VOC 格式。我一般导出 YOLO 分割格式,因为后续要在 YOLOv8 上训练。导出后可以用 Ultralytics 直接开训,配套的代码也很简单:
from ultralytics import YOLO model = YOLO("yolov8s-seg.pt") model.train(data="dataset.yaml", epochs=100, imgsz=640)训练完成后,用模型在验证集上评估,如果 mAP 低于 0.5,我通常会回头检查两方面:一是自动标注阶段是否对某些类别产生了系统性的漏检或误检,二是人工修正阶段是否漏掉了某些容易混淆的类别。这两个问题反馈到数据层面,比盲目调训练参数更管用。
5. 常见问题与排查技巧实录
5.1 Grounded-SAM 推理速度慢或显存不足
症状:批处理到一半程序崩溃,或者显存占用打满导致卡顿。
解决思路分三步走。第一,把 SAM 的模型类型从vit_h换成vit_b,在 Loading 阶段直接改配置即可。速度提升非常明显,1080p 图片从单张约 1.2 秒降到约 0.4 秒,显存占用也从 8GB 以上降到 5GB 左右。第二,把图片长边缩放到 1280 或 1024,SAM 对长边的分辨率上限本来就有约束,再高也是等比例缩放,不会带来额外精度收益。第三,如果仍然爆显存,把 batch size 降到 1,逐张处理。
5.2 检测漏检或误检过多
症状:某个类别经常没框出来,或者框出来后掩码把背景也包进去了。
最常见的根因是提示词写得不够准确。可以先针对漏检的样本单独调试提示词,在 Grounded-SAM 官方推理脚本里做几次迭代测试。另外就是阈值设置问题,把box_threshold降到 0.25 可以明显提升召回,但代价是高置信度的误检增多。我们最终用的策略是“用稍低的阈值保证召回,用 X-AnyLabeling 人工删除误检”。因为删除误检比补漏检要快得多,毕竟误检是少数,漏检是万一看漏了就更麻烦。
5.3 标注格式转换出错或坐标偏移
症状:在 X-AnyLabeling 里打开的自动标注结果出现跑偏、错位、比例错误。
先检查坐标系,COCO 的坐标是绝对像素,LabelMe 的坐标也是绝对像素,中间没有归一化,问题通常出在 JSON 结构不对。仔细检查shapes数组里的points格式是否嵌套正确,X-AnyLabeling 需要[[x1, y1], [x2, y2], ...]的形式。如果导出 YOLO 格式后训练报坐标越界错误,我记得要去检查是否做了归一化,YOLO 分割模式下坐标是归一化到 0-1 之间,别忘了除以图像宽高。
5.4 autodistill 初始化时网络问题
症状:首次实例化 GroundedSAM 时卡住,或者报网络连接错误。
autodistill 首次运行要下载 Grounding DINO 和 SAM 的权重,如果网络波动,下载会中断,导致环境被锁死。我建议提前手动下载权重文件,放到~/.cache/autodistill/或者直接改代码里的权重路径。另一个坑是OPENAI_API_KEY缺失时,有的 autodistill 版本会在初始化时尝试连接 API 并失败。如果你不想用外部 API,可以显式传入空字符串或直接改配置跳过 prompt 优化。
5.5 X-AnyLabeling 打开大图或大量文件卡顿
症状:单张高分辨率图(比如 4000x3000)打开要好几秒,缩放卡顿明显。
这是 Qt 渲染大图的通病。我的方法是先用图像预处理脚本把全量图片长边缩放到 1920 左右再进人工审核流程,训练时再适配上采样还原尺寸。对于只做检测不做分割的项目,1920 的长边足够看清目标,标注出来的坐标在训练小模型时完全够用。如果 1920 觉得不够清晰,可以提高到 2560,但要接受一定的卡顿。
6. 最后分享一点我个人的实操心得
折腾了这么久的自动标注,最大的体会就是不要迷信模型能力,也别低估人工审核的价值。Grounded-SAM 确实能生成高质量的初始标注,但它对提示词的依赖远超大多数人的预期。提示词设计得好,自动标注的质量可以高到几乎不需要修正;提示词设计不好,效率反而不如直接手工标注。所以我在每个新项目启动时,都会专门抽出半天时间测试提示词和阈值,把这个基础打好再上量。
另一个心得是工具的关联比工具本身更重要。刚开始我只装了 X-AnyLabeling,后来才发现它跟 autodistill、Grounded-SAM 可以形成一条完整链路。单独用任何一个工具,效率都只是“比手标快一点”,但把它们串起来,效率就是成倍的提升:Grounded-SAM 负责自动生成,autodistill 负责批量和格式,X-AnyLabeling 负责审核与修正,三者无缝衔接之后,整个标注流程才真正从“人工绘制”跃进到“人工审阅”。
最后一个小技巧,每次生成完一批标注,都要做一个抽样质检。我会随机抽 5-10% 的图片看一眼标注结果,确认没有系统性偏差(比如某个类别整体漏检、掩码明显偏大或偏小)再进入下一环节。这个习惯帮我挡掉了好几次大事故——有一次提示词没写好,整个数据集的“安全帽”类别几乎没有标注出来,幸好抽样时发现了,省去了后面前功尽弃的代价。自动标注只是一个工具,真正决定数据质量的还是你审核的细致程度。