ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

自动标注闭环实战:从Grounded-SAM到autodistill

自动标注闭环实战:从Grounded-SAM到autodistill 开头直接进入正题不讲废话。我先说清楚这篇文章是什么这是一份把 X-AnyLabeling、autodistill 和 Grounded-SAM 串起来做自动标注的完整实战记录覆盖从环境部署到批量出标签再到人工修正的全流程。前前后后折腾了小半个月踩了不少坑把能省的弯路都省掉了适合做目标检测、实例分割、半监督训练的朋友直接参考。如果你想搞清楚这几个工具分别在流程里扮演什么角色以及怎么把它们真正跑起来而不是停留在 demo 阶段这篇文章应该能帮到你。1. 工具选型解析为什么是这三个而不是别的先聊一个很多人忽略的问题工具链不是越新越好而是组合起来能覆盖完整流程才好。市面上做自动标注的工具其实不少像 Label Studio、CVAT、Roboflow 都有各自的自动化能力但在“本地私有化部署 大模型辅助标注 可控的人工修正 伪标签批量产出”这条完整链条上我反复对比后还是选了 X-AnyLabeling、autodistill 和 Grounded-SAM 这个组合。1.1 三个工具的核心定位与分工用一句话分别概括这三个东西X-AnyLabeling 是一个交互式标注工具核心价值在于能把深度学习模型直接跑进标注界面里实现“模型预标注 人工微调”的工作模式。它内置了 Grounding DINO、SAM、YOLO 系列等模型推理能力也支持你加载自己训练好的模型来做预标注。换句话说它是这套流程的“工作台”和“人工兜底环节”。Grounded-SAM 是 Grounding DINO 和 SAMSegment Anything Model的组合先用文本提示词检测目标得到边界框再把框送到 SAM 生成精细分割掩码。它解决的是“从零开始产生高质量伪标签”的问题是自动标注的第一棒。autodistill 是一个面向“教师模型打标签学生模型学习”的自动化标注框架。它把数据集的构建、教师模型推理、标签导出和学生模型训练封装成了标准流程。它是连接“模型产出的标签”和“可用训练数据”的桥梁。罗列完各自定位分工就很清楚了Grounded-SAM 负责批量生成初始标注X-AnyLabeling 负责让人工介入修正错误autodistill 负责把修正后的数据标准化导出并支持学生模型迭代。单看任何一个是残缺的但连起来就是一条能落地的自动标注流水线。1.2 方案选型的四个关键考量点我做选型对比的时候核心考量的不是谁的功能列表更长而是以下四个问题是否支持 Windows 本地部署。很多类似工具对 Windows 支持很差而 X-AnyLabeling 是基于 PyQt5 的图形界面工具Windows 上可以直接跑。这一点对没有 Linux 服务器资源的小团队和个人开发者很关键。是否支持自有模型接入。Grounded-SAM 是通用模型X-AnyLabeling 可以加载自定义模型。这意味着你在某个特定场景下的专用模型也能在标注界面里做预标注而不是只能依赖通用模型。标签导出格式是否灵活。autodistill 支持导出为 COCO、YOLO 等常见格式同时也能配合各种检测、分割框架使用。这决定了前期标注成果能不能顺利喂给后边的训练流程。是否存在可复现的坑。这三个工具网上教程虽然不算多但社区实践还算活跃遇到问题有迹可循。相比之下一些界面漂亮但文档为空的商业工具出了问题只能对着英文 issue 干瞪眼。选了这套组合之后实际跑下来也确实稳定至少不会出现某个环节完全黑盒的情况。2. 环境部署实录X-AnyLabeling 源码运行的正确姿势部署阶段是最劝退新手的部分尤其是 X-AnyLabeling直接 pip 安装虽然也能用但要加载自定义模型和部分卡模型联动功能时还是得从源码跑。这里记录一下我在 Windows PyCharm 环境下的完整部署过程以及几个容易翻车的点。2.1 环境准备与依赖安装基础环境如下凡是版本不一致的地方都可能出问题尽量对照调整操作系统Windows 10/11Python3.8 或 3.9不建议用 3.10后面会有说明显卡NVIDIA 显卡显存建议 8GB 以上用 CPU 推理也不是不行但速度会让你怀疑人生CUDA建议 11.8 或对应显卡驱动支持的版本PyCharm专业版或社区版均可核心是配置好虚拟环境操作步骤如下创建虚拟环境并激活conda create -n anylabeling python3.9 conda activate anylabeling克隆源码并安装依赖这里用的是国内镜像加速git clone https://github.com/CVHub520/X-AnyLabeling.git cd X-AnyLabeling pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple在 PyCharm 中配置解释器为刚才创建的 conda 环境然后直接运行main.py。这里有一个很容易被忽视的细节requirements.txt 里有些包的版本并不宽松直接装最新版大概率会冲突。我第一次装的时候就是 torch 版本被升级到了 2.x结果部分模型推理接口不兼容整个界面能打开但模型一加载就崩溃。后来把 torch 重新装回 1.13.1 才恢复正常。稳妥起见建议在安装依赖时加上--no-deps逐个人工安装核心依赖或者直接按 requirements.txt 的版本号约束来装。2.2 模型加载路径与常见坑X-AnyLabeling 的模型管理在界面的右上角加载模型时会先检查本地缓存没有的话会自动下载。这里有个问题默认下载源在国外很多用户卡在下载进度条上半天没反应。我的解决办法是手动下载模型文件放到models目录下再从界面里加载本地模型。手动放置模型需要注意目录结构要和源码里的加载逻辑对应。以 Grounding DINO 为例模型文件通常放在models/groundingdino/下同时需要对应的配置文件。界面加载时如果提示找不到配置文件多半是路径层级不对。还有一点不算坑但容易被忽略X-AnyLabeling 的快捷键非常高效但新手往往不知道导致在界面里点来点去。我先把常用的几个列出来后面的实操会反复用到快捷键功能A / D 或方向键切换上一张 / 下一张图片Ctrl S保存当前标注结果Ctrl Z撤销上一步标注操作Q / E放大 / 缩小画布鼠标滚轮缩放画布Delete删除当前选中的标注框想查看完整快捷键列表可以在设置里找到快捷键表建议先花十分钟过一遍。后面人工修正环节的体验差异很大程度上就取决于你对快捷键的熟悉程度。2.3 PyCharm 运行源码的调试技巧用 PyCharm 跑源码有个小技巧不要直接右键运行main.py而是先配置一个 Run Configuration在 Environment variables 里加上PYTHONUNBUFFERED1。不然 Win 系统下控制台输出的日志可能不能实时刷新遇到模型加载卡住你都不知道它卡在哪一步。另外如果界面启动时报错qt.qpa.plugin: could not load the Qt platform plugin windows通常是因为 PyQt5 的路径没有正确包含 platforms 插件目录。这个问题的根源是虚拟环境里 PyQt5 和其他 GUI 相关的包版本冲突。我自己遇到时是把已经装好的PyQt5、PyQt5-sip、PyQt5-Qt5三个包全部卸载重装重新执行pip install PyQt55.15.9解决的。3. Grounded-SAM 流水线从文本提示到精细掩码部署好标注工具之后事情并没有结束。真正实现“从零开始自动标注”需要一个能批量产出伪标签的推理引擎。这里就是 Grounded-SAM 的主场。3.1 Grounding DINO SAM 的工作机制Grounded-SAM 这个名字拆开看就是两个模型的串联。第一个是 Grounding DINO负责做开集目标检测输入是图片和文本提示词输出是目标的边界框。第二个是 SAM负责做分割输入是图片和边界框也可以输入点输出是精确到像素的掩码。为什么要拆成两步串联而不直接用某个单模型一步到位核心原因是目前没有一个模型能在“任意文本提示词理解”和“像素级分割质量”两个维度上同时达到理想效果。Grounding DINO 擅长理解语义把“找图中的红色汽车、电线杆、行人”这类自然语言描述变成框SAM 擅长几何分割给定一个框之后它能把目标边缘细节切得比较干净。两个模型串联是当下工程上的最优解而不是因为谁的代码里恰好有两个模型。用生活化的方式理解Grounding DINO 相当于一个眼尖的向导看到图上哪里有符合描述的东西就指给你看SAM 相当于那个拿着剪刀的裁缝向导指哪里它就精准地剪出那个东西的轮廓。两步配合才能从一句“帮我标出所有穿蓝色衣服的人”变成一份能直接用来训练分割模型的掩码标注。3.2 最小可运行的 Grounded-SAM 部署方案部署 Grounded-SAM 的大体步骤是下载源码、准备模型权重、安装依赖、运行脚本。这里给一个基于官方仓库的最小方案。# 克隆仓库 git clone https://github.com/IDEA-Research/Grounded-Segment-Anything.git cd Grounded-Segment-Anything # 创建环境建议独立环境不要和 X-AnyLabeling 混用 conda create -n grounded-sam python3.9 conda activate grounded-sam # 安装依赖 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install opencv-python pillow matplotlib pycocotools pip install githttps://github.com/facebookresearch/segment-anything.git需要下载的模型包括 Grounding DINO 的权重groundingdino_swint_ogc或更大的swinb以及 SAM 的权重sam_vit_h.pth、sam_vit_l.pth或sam_vit_b.pth。其中 Grounding DINO 的权重在自己的仓库里SAM 的权重在 Meta 的 model zoo。下载后放到Grounding-Segment-Anything根目录下然后在推理脚本中指定路径。这里有一个影响标注质量的关键参数box_threshold 和 text_threshold官方默认值一般是 0.25 和 0.20。这两个值控制的是检测的置信度门槛。在实际项目里我不会直接沿用默认值而是先用 10 到 20 张代表性图片跑一遍观察漏检多还是误检多漏检多该标出来的目标没标出来适当降低 box_threshold 到 0.2 左右。误检多把无关区域也框出来了适当提高 box_threshold 到 0.3 甚至 0.35。text_threshold 调整同理它影响的是文本语义匹配的严格程度调高会让模型更保守。这个微调过程非常值得做因为它在不改代码的前提下就能显著影响标注质量是自动标注流水线里性价比最高的一步。3.3 批处理推理与输出格式官方 demo 通常一次处理一张图片但在实际项目中你手里往往有成百上千张图。这时候就得写一个批处理脚本。我核心的做法是遍历整个图片文件夹对每张图片执行同样的推理流程并记录输出框、分数和掩码。实际跑的时候建议把输出保存为 JSON 文件每条记录包含图片路径、检测到的类别名、边界框坐标、置信度分数和掩码的 RLE 编码。不要直接存成 PNG 掩码图一方面 RLE 体积更小另一方面后续转成 COCO 或 YOLO 格式时 RLE 是标准中间格式能省去很多格式转换的时间。关于速度用 8GB 显存的 GPU 跑 Grounding DINOswint 模型 SAMvit_b时一张 1200x800 的图片大约需要 2 到 4 秒。如果换成 vit_h 的 SAM显存占用和耗时都要翻倍。所以批量标注前要根据自己的显存量选择模型规格不要盲目追求大模型。4. autodistill 实战教师模型打标签学生模型跟上伪标签批量产出了然后呢接下来需要一套体系来管理这些标签、把它们转换成能直接喂给训练流程的数据集并且支持通过小模型迭代达到最终自动化。这就是 autodistill 存在的价值。4.1 autodistill 的核心设计理念autodistill 的根本思路是知识蒸馏思想在数据标注层面的具体实现。你可以简单理解成一个“老教师带新徒弟”的机制教师模型通常是大而强的视觉模型依靠推理产出标注标注结果作为训练数据输入给学生模型通常是轻量、高效的模型学生模型在训练后逐步逼近教师模型在该任务上的能力。等到学生模型的表现可以接受时后续的标注工作就可以完全交给这个轻量模型来做速度和成本都会下降。在代码实现上autodistill 抽象了三个核心模块BaseModel教师模型本质上就是“拿图出标签”的推理封装。TargetModel学生模型输入标注数据输出训练后的模型。Dataset统一的数据结构管理图像和标签的对应关系。Ontology定义了标签体系决定教师模型能识别哪些类别。使用 autodistill 的常见模式是定义好 Ontology用 BaseModel 给图片打标签把生成的标签交给Dataset管理然后用TargetModel开展训练。这种设计让“从预标注到训练再到下一轮推理”的闭环非常自然地运转起来。4.2 一个典型的使用流程以目标检测为例假设要用 Grounding DINO 作为教师模型给一批图片打标签然后训练一个 YOLO 系列的学生模型。核心代码如下from autodistill.detection import DetectionBaseModel, DetectionTargetModel from autodistill_grounding_dino import GroundingDINO from autodistill_yolov8 import YOLOv8 # 定义标签体系 ontology { person: person, car: car, traffic_light: traffic light } # 初始化教师模型 base_model GroundingDINO(ontologyontology) # 使用教师模型为数据集打标签 dataset base_model.label( input_folderimages/raw, output_folderimages/labeled ) # 初始化学生模型并利用标注数据训练 target_model YOLOv8(ontologyontology) target_model.train(dataset, epochs50)这段代码看起来简洁但背后有几个值得细看的点。第一ontology的 key 和 value 可以不一致比如traffic_light: traffic light这种写法就能让标签体系内部用短名称而传给 Grounding DINO 的文本提示用更完整的自然语言描述语义匹配更准确。第二label方法会在output_folder下生成图片和对应的标签文件标签格式取决于教师模型但如果后续要训练 YOLO最好在train前显式转换为 YOLO 格式。第三dataset对象内部会建立图片与标签的索引关系这个对象是后续训练和导出的中枢。跑这个过程时我建议图片不要一次性全上先放 20 张进去验证整个链路是否通畅。确认无误后再放全部数据否则格式错了、路径错了后期排查成本很高。4.3 伪标签质量控制与清洗策略自动标注产出的伪标签不可能百分百准确所以质量把控必须作为一个独立环节来设计。我在实际项目中总结出了三个层次的清洗方法第一层是规则过滤。对框的尺寸、长宽比、置信度分数做统计凡是明显偏离正常分布的记录直接剔除。比如一张 1280x960 的图上如果出现了一个小于 10x10 像素的框通常不是目标而是噪声。置信度低于阈值的框也直接删除。第二层是人工抽检。无论自动标注多快都建议按 5% 到 10% 的比例抽检。抽检不是随便看看而是结合统计指标来判断整体质量。假设 1000 张图抽了 80 张如果超过 5 张有明显错误说明教师模型的这套参数设置在这个场景下并不合适需要回到参数调优这一步而不是继续往下游走。第三层是模型闭环校验。用自动标注的数据先训练一版学生模型然后用这个学生模型重新对同一批图片推理和教师模型的标注结果做对比。两者差异较大的图片就是需要关注的困难样本。这种方式能精准定位模型边界也是后续迭代的数据来源。我在第一次做项目时直接跳过了这三层清洗把伪标签当成真实标签丢进了训练流程结果模型性能明显低于预期。后来排查发现大约有 20% 的标签存在边界框偏移或类别误判的情况。从那以后清洗环节我再也没有跳过。5. 三链路整合跑通一套完整的自动标注闭环现在三个工具都摸清了但很多人还是卡在“每个工具都会用但不知道它们怎么衔接”这一步。这一部分把整合思路和实战过程完整串起来。5.1 整体整合链路整个自动标注流水线按照数据流向可以拆成以下环节用 Grounded-SAM 对原始图片执行批量预标注产出带置信度分数的伪标签。将 Grounded-SAM 的推理结果导入 X-AnyLabeling人工修正错误标注。导出修正后的标注结果转换为 autodistill 标准数据集格式。在 autodistill 中定义 Ontology用修正后的数据训练学生模型。学生模型继续在未标注数据上推理产出新一批伪标签再次导入标注工具进行抽检循环迭代。这套流水线强调的不是某一家的模型有多强而是“自动预标注降低人工量人工修正保证质量下限小模型迭代降低推理成本”这一整套逻辑自洽的循环。5.2 从 Grounded-SAM 到 X-AnyLabeling 的标签导入这一步是很多教程没讲清楚的地方。Grounded-SAM 产出的标签要拿到 X-AnyLabeling 的界面里人工修正关键是格式转换。X-AnyLabeling 支持的导入格式中稍微省事的做法是先把 Gounded-SAM 的结果整理成一个中间 JSON然后使用标注工具的“导入”功能加载。以目标检测框为例转换后的格式大致如下[ { image_path: images/image_001.jpg, labels: [ {category: car, bbox: [142, 200, 560, 380], score: 0.87}, {category: person, bbox: [300, 150, 120, 250], score: 0.79} ] } ]这个 JSON 描述的是一张图片上的多个标注框bbox用的是x_min, y_min, x_max, y_max格式。X-AnyLabeling 读取后会把对应的框画在图上之后你就可以直接在这个基础上增删调整。这里有一个细节Grounded-SAM 产生的框是 xyxy 格式而很多检测框架用的是 xywh 格式导入导出时务必确认清楚不然会出现框的位置错位问题肉眼看着还好但训练时 loss 完全不对。另外导入前建议把低置信度的框先过滤掉保留 score 较高的框作为人工修正的底稿这样打开 X-AnyLabeling 的时候画面干净很多。低分框虽然可能是真目标但在人工阶段再快速补画比在一堆误检框里挑真目标更高效这是我的实际感受。5.3 人工修正阶段的效率管理修正阶段最容易忽略的是流程管理而不是手感。建议把修正任务按批次划分每天设定明确的完成量。此外在 X-AnyLabeling 中通过“仅显示未标注图片”和“跳转到下一张未标注图片”这两个操作来维持节奏很有效。界面下方如果有标注进度统计也能直观反映项目整体进度。对于修正中频繁出现的类别错误建议在标注的同时顺手记录错误类型比如“人误标为行人”、“轿车误标为卡车”等。统计一段时间后你会发现问题集中在少数几个类别上。这时候可以针对性地调整 Grounded-SAM 的提示词描述例如把容易混淆的类别提示词写得更具体重新跑一遍预标注修正工作量往往能再降一个台阶。5.4 从 X-AnyLabeling 到 autodistill 的数据流人工修正完的标注结果要进入 autodistill 训练需要先把标注数据导出为标准数据集格式。X-AnyLabeling 可以导出为 COCO 格式也有不少用户导出为 YOLO 格式再手写转换脚本。稳妥的做法是先导出为 COCO JSON然后写一个小脚本把 COCO 转换成 autodistill 的 Dataset 目录结构dataset/ ├── images/ │ ├── image_001.jpg │ └── image_002.jpg └── labels/ ├── image_001.txt └── image_002.txt其中 labels 下的 txt 文件是 YOLO 格式每行对应一个目标内容为class_id x_center y_center width height坐标均为归一化后的 0 到 1 数值。autodistill 的 YOLOv8 TargetModel 和这个目录结构配合良好训练时直接指定dataset文件夹路径即可。这个环节最常见的错误是类别 id 错位。比如在 X-AnyLabeling 里类别顺序是[car, person, traffic_light]但转成 autodistill 数据集时类别索引没有按同样顺序写入结果就会把车标成人、人标成车。在写转换脚本时务必在第一行输出一个类别映射表并在转换后抽样检查几次确认每个类别 id 都对应正确的标签。6. 常见问题与排查技巧实录那些文档里不会写的事这部分把我在实际项目中遇到的典型问题和解决办法整理成速查表很多内容不是看文档就能发现的而是真正跑一轮才能踩到。6.1 问题速查表现象可能原因解决方案X-AnyLabeling 启动后界面白屏PyQt5 版本冲突重装 PyQt5 5.15.9并清理并重装相关插件包加载模型时进度条卡住模型下载源在海外网络受限手动下载模型文件并放到 models 对应目录Grounding DINO 检测结果全为空box_threshold 和 text_threshold 过高调低阈值到 0.2 / 0.2 附近或检查提示词描述是否与目标匹配掩码与目标边缘不贴合使用了过大的 SAM 模型但输入图像过大先对图像做缩放预处理或使用较小的 SAM 模型配合精细 mask 参数autodistill 训练时类别数量不匹配数据集中的类别 id 与 ontology 不一致检查 YOLO 标注文件的类别索引和 ontology 列表顺序一一对应学生模型在部分图片上表现较差教师模型伪标签中存在系统性误检抽检伪标签针对问题类别优化提示词或扩充困难样本批量推理显存溢出OOM单张图片过大或一次加载多张到 GPU降低批次图片分辨率或逐张推理6.2 Grounded-SAM 提示词调优的实操心得提示词是 Grounded-SAM 中最需要打磨的部分。我一开始用的是简单名词比如person、car效果还行但漏检较多。后来换成带上下文描述的形式比如a person in blue uniform或者a red car parked on the street检测效果明显提升。原因是 Grounding DINO 基于文本语义进行检测更丰富的上下文信息能帮助模型锁定目标区域。而且提示词不一定要遵守固定的语法可以同时传入多组描述模型会自动选择匹配的目标。我通常的做法是根据数据分布枚举所有常见形态比如person, pedestrian, man in blue uniform, woman in security uniform用逗号分隔覆盖面会广很多。不过提示词太长也会引入噪声需要根据实际效果迭代。6.3 显存管理的优化技巧显存不够大概是自动标注环节遇到频率最高的问题。我的建议按优先级排序如下降低输入分辨率。Grounded-SAM 对 1080P 以上图片的检测效果确实好一些但分辨率下降后对检测结果的实际影响小于多数人的预期而显存占用下降非常可观。分段执行流程。检测阶段用 Grounding DINO 时关闭 SAM分割阶段再加载 SAM两个模型不同时驻留显存。使用批量量化或混合精度。如果显存仍然不足可以在推理脚本中启用 fp16 模式半精度推理的显存占用约是 fp32 的一半精度损失对标注任务来说通常可接受。使用分块推理。对超大图比如卫星影像可以先切成小块分别推理再把标签拼回去。这个操作略微繁琐但能有效绕开显存限制。6.4 数据闭环的持续迭代策略整个流程搭建完成后最关键的事情是持续迭代而不是一次跑完就收工。我的做法是每两周跑一轮循环用当前学生模型在新增图片上推理生成伪标签后抽检修正再把这个批次的数据并入训练集训练新版本学生模型。通过这种方式模型会越来越贴心当前场景的标注和检测能力。一开始教师模型给出的结果可能需要人工修改很多但跑过两三轮之后漏检率、误检率明显下降人工介入的比例也在下降。这个变化不是模型本身的进步而是数据分布逐渐被模型“消化”了每一轮循环都在强化模型对当前场景的适配度。这就是自动标注流水线的终态不是完全无人化而是把人力从大量重复劳动中释放出来集中到处理边缘和疑难样本上。最后分享一个自己的经验不要一开始就追求完全自动化。先把流水线跑通、跑稳、把质量检控指标建好再用经历几次迭代逐步减少人工环节这个顺序才对。反过来如果一开始就设定“全自动、无人看管”的目标通常会在某个环节翻车最后还得回炉重造耗时更长。我在实际项目中最大的体会是自动标注的价值不是取代标注员而是把标注效率提升一个数量级的同时将人的精力集中在真正需要判断力的样本上。工具链本身不难难的是把每个环节的配合理顺把质量标准建起来。希望这份实战记录能让你少走几趟弯路直接把这套流程用起来。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进