news 2026/10/12 2:26:56

X-AnyLabeling 挂载 yolox-s-onnx 自动标注实战:从导出到迭代闭环

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
X-AnyLabeling 挂载 yolox-s-onnx 自动标注实战:从导出到迭代闭环

简介:这份资源面向计算机视觉开发者与数据标注从业者,提供X-AnyLabeling自动标注所需的YOLOX-S模型文件,用于在标注工具中加载ONNX模型实现目标检测的预标注,减少人工画框的重复劳动。压缩包共2个文件,包含1个yaml配置文件与1个onnx模型文件,前者记录网络结构与超参数,后者为可直接推理的跨平台模型,整体约31.83MB,体积轻量便于快速部署。目前已有2509人学习下载,说明该组合在自动标注场景中具有较高的实用参考价值。读者可借助该模型在X-AnyLabeling中完成图像预测与初步标注框生成,再结合人工审核修正,从而提升大规模数据集标注的效率与一致性,适合希望将深度学习模型与标注工具结合使用的中级用户参考实践。

1. 从手工框到自动标注:X-AnyLabeling 挂载 yolox-s-onnx 到底省了什么

如果你做过目标检测数据集,大概率经历过这样的循环:打开标注工具,一张张图拉框,拉到手酸,回头一看才标了两百张,而模型训练动辄要几千张。X-AnyLabeling 的 yolox-s-onnx 自动标注模型,解决的正是这个循环里最枯燥的一段——让一个已经训练好的轻量检测模型先跑一遍,把预测框吐成预标注文件,人只需要在它的基础上改,而不是从零画。

这个组合的定位很清晰:X-AnyLabeling 是标注前端,负责加载图片、渲染框、导出格式;yolox-s 是检测后端,s 表示 small 版本,参数量和推理耗时都压得比较低;onnx 是中间格式,让模型脱离训练框架,在标注工具里直接推理。三者拼起来,就是一条「本地跑、不依赖云端、可离线」的预标注流水线。

它适合谁?手上有一批领域图片、已经有一个还凑合的检测权重、想快速把标注产能提上去的人。不适合谁?完全没有基础权重、指望零样本直接标出高精度框的人——自动标注的上限,永远是你喂给它的那个模型的上限。下面按「怎么把这条链路跑通、参数怎么调、坑在哪」的顺序拆开讲。

2. 把 yolox-s 导出成 onnx:输入输出节点与三个必调参数

2.1 为什么选 yolox-s 而不是更大的模型

自动标注场景对精度的容忍度比线上推理高,因为框出来之后还有人复核。但对速度的容忍度很低,标注是交互式操作,一张图等三秒和等三百毫秒,体验差一个量级。yolox-s 在这个权衡里是甜点:输入 640×640 时,单张 GPU 推理通常在十几毫秒量级,CPU 上也能跑到可接受的范围,权重文件小,导出 onnx 后体积可控。

更大的模型比如 yolox-m/l/x,精度提升有限,但在标注工具里会明显拖慢节奏。我的经验是:先用 s 跑一轮,看预标注的召回够不够,如果漏检严重再考虑换大模型,而不是一上来就上大的。

2.2 导出 onnx 的最小脚本

导出这一步的关键是固定输入尺寸、固定 batch、明确输出节点名,否则 X-AnyLabeling 加载时会找不到张量。下面是一个常见的导出脚本结构:

import torch from yolox.exp import get_exp from yolox.models import YOLOX # 加载配置和权重,exp 文件决定网络结构 exp = get_exp("exps/default/yolox_s.py", None) model = exp.get_model() ckpt = torch.load("yolox_s.pth", map_location="cpu") model.load_state_dict(ckpt["model"]) model.eval() # 固定输入:batch=1, 3通道, 640x640 dummy = torch.randn(1, 3, 640, 640) torch.onnx.export( model, dummy, "yolox_s.onnx", input_names=["images"], # 输入节点名,加载时要对上 output_names=["output"], # 输出节点名 opset_version=11, # 11 兼容性好,别盲目追高 dynamic_axes=None, # 固定尺寸,避免动态轴带来的解析麻烦 ) print("export done")

逻辑说明:get_exp读的是网络结构定义,必须和训练时用的 exp 一致,否则权重加载会报 key 不匹配。model.eval()不能省,否则 BN 层走训练模式,输出会飘。opset_version选 11 是稳妥值,部分推理后端对更高 opset 的支持参差不齐。

参数说明:input_names和output_names是给下游加载器用的标识,X-AnyLabeling 在配置里要填对应的名字。dynamic_axes=None表示输入尺寸写死,自动标注场景不需要动态 batch,写死反而省去很多解析麻烦。

2.3 导出后必须做的两项验证

导出完不要直接扔进标注工具,先用 onnxruntime 跑一遍,确认输出形状和数值合理:

import onnxruntime as ort import numpy as np sess = ort.InferenceSession("yolox_s.onnx") inp = np.random.randn(1, 3, 640, 640).astype(np.float32) out = sess.run(None, {"images": inp}) print(out[0].shape) # 期望类似 (1, 8400, 85)

如果输出形状里出现了动态维度(比如 batch 显示为字符串),说明导出时没固定住,回到上一步检查dynamic_axes。如果数值全是 NaN,多半是权重没加载对或者输入没归一化——注意这里只是形状验证,真实推理时输入要做和训练一致的归一化。

3. 在 X-AnyLabeling 里配置 onnx 模型:路径、类别与阈值

3.1 模型配置文件的字段含义

X-AnyLabeling 加载自定义模型通常靠一份配置文件,核心字段包括模型路径、模型类型、输入尺寸、类别列表、置信度阈值、NMS 阈值。下面是一个典型结构:

type: yolox model_path: /path/to/yolox_s.onnx input_width: 640 input_height: 640 conf_threshold: 0.35 iou_threshold: 0.45 classes: - person - car - dog

字段说明:type告诉工具用哪套后处理逻辑,yolox 的输出解码和 yolo 系列略有差异,选错会导致框全乱。conf_threshold是置信度门槛,自动标注建议先调低一点,宁可多出框让人删,也别漏掉目标让人补。iou_threshold控制 NMS 的合并力度,重叠目标多的场景要适当调高。

3.2 类别列表必须和训练时严格对齐

这是最容易翻车的地方。类别顺序错一位,所有框的标签就整体错位,而且错得很隐蔽——框的位置是对的,只是名字不对,复核时容易漏看。判断方法:拿一张确定包含某类目标的图,看预测出来的标签是不是该类。如果整体偏移,就是类别列表顺序和训练时的classes不一致。

3.3 阈值怎么起步

我的习惯是首轮conf_threshold设 0.25 到 0.3,iou_threshold设 0.45。跑十几张图看效果:如果框太密、大量重复框,说明 NMS 不够狠,把 iou 降到 0.4;如果明显目标都没框出来,先把 conf 降到 0.2 看是不是阈值卡太死,如果还是不出,那就是模型本身没学到,调阈值没用。

提示:自动标注的阈值和线上推理的阈值不是一回事。线上追求准,标注追求全,两者目标相反,不要直接套用线上那套参数。

4. 批量预标注与结果复核:把产能真正提上来

4.1 批量推理的目录组织

单张跑通之后,下一步是批量。把待标注图片放一个目录,输出预标注文件放另一个目录,脚本遍历即可:

import os import cv2 import numpy as np import onnxruntime as ort sess = ort.InferenceSession("yolox_s.onnx") img_dir = "images" out_dir = "prelabels" os.makedirs(out_dir, exist_ok=True) for name in os.listdir(img_dir): if not name.lower().endswith((".jpg", ".png")): continue img = cv2.imread(os.path.join(img_dir, name)) h, w = img.shape[:2] # letterbox 到 640x640,保持比例 scale = min(640 / w, 640 / h) resized = cv2.resize(img, (int(w * scale), int(h * scale))) canvas = np.full((640, 640, 3), 114, dtype=np.uint8) canvas[:resized.shape[0], :resized.shape[1]] = resized inp = canvas[:, :, ::-1].transpose(2, 0, 1)[None].astype(np.float32) / 255.0 out = sess.run(None, {"images": inp})[0] # 后续解码 + NMS + 坐标还原,写入预标注文件 # 这里省略解码细节,重点是 letterbox 和坐标反变换要成对

逻辑说明:letterbox 是保持长宽比缩放再补边,避免直接 resize 导致目标变形。补边值 114 是常见约定,要和训练时的预处理一致。坐标还原时要用同一个 scale 反算,否则框会整体偏移。

参数说明:scale是缩放比例,反变换时除以它。补边的偏移量在反变换时也要减掉,这一步漏了框会整体平移。

4.2 预标注文件的格式

X-AnyLabeling 支持导入多种格式的预标注,常见的是它自己的 json 格式或标准 COCO/VOC。批量脚本输出的格式要和工具的导入接口对上。如果格式不对,工具会静默跳过,表现为「导入了但没框」,这时候去检查输出文件的字段名。

4.3 复核环节怎么省力

预标注不是终点,复核才是。我的做法是按置信度排序复核:先看高置信度的,快速扫过确认;重点看中等置信度的,这些是模型犹豫的地方,最容易出错;低置信度的直接批量删掉或重标。这样比随机翻图效率高得多。

5. 避坑与排查:自动标注最常见的五类翻车

5.1 框整体偏移或缩放错位

现象:所有框都比真实目标大一圈、小一圈,或者整体平移。原因:letterbox 的 scale 和 padding 在反变换时没成对使用,或者训练时的预处理和推理时不一致。解决:把预处理和反变换写成一对函数,输入输出用同一组参数,别在两处各写一遍。

5.2 标签整体错位

现象:框的位置对,但类别名全错或整体偏移一位。原因:类别列表顺序和训练时不一致,或者配置文件里 classes 少写/多写。解决:拿一张确定类别的图验证,逐位核对 classes 列表,顺序必须和训练时的定义完全一致。

5.3 输出全是空或全是重复框

现象:一张图要么一个框都没有,要么同一个目标叠了七八个框。原因:前者多半是 conf 阈值过高或输入归一化不对;后者是 NMS 没生效或 iou 阈值过高。解决:先确认输入归一化(除以 255、通道顺序 RGB)和训练一致,再调 NMS 参数。如果 NMS 完全没起作用,检查后处理代码里有没有真的调用。

5.4 onnx 加载报节点找不到

现象:工具加载模型时报输入或输出节点名不匹配。原因:导出时的input_names/output_names和配置文件里填的不一致。解决:用 onnxruntime 打印sess.get_inputs()和sess.get_outputs()的名字,照着填,别凭记忆。

5.5 CPU 推理慢到无法交互

现象:每张图要等好几秒,标注节奏被打断。原因:onnxruntime 默认可能没开多线程,或者用了 CPU 但模型本身偏大。解决:创建 session 时显式设置线程数,确认是否可用 GPU;如果只能用 CPU,考虑换更小的输入尺寸或更小的模型。

注意:自动标注的结果一定要人工复核后再进训练集。把预标注直接当 ground truth 用,等于把模型的错误固化进下一轮训练,越训越偏。

6. 让预标注越用越准:迭代闭环与一个实用技巧

自动标注真正的价值不在第一轮,而在闭环。第一轮用现有模型标一批,人工修正后得到高质量标注,拿这批数据微调模型,再用新模型标下一批。每一轮模型的召回和精度都会往上走,人工修正的量逐轮下降。这个循环跑起来之后,标注产能是复利增长的。

具体怎么做?第一轮挑 200 到 500 张有代表性的图,用 yolox-s-onnx 预标注,人工精修,训练一版新权重,重新导出 onnx 替换掉工具里的模型。第二轮再标 500 张,此时预标注的可用率会明显提升。注意每轮都要留一部分图不参与训练,用来验证预标注质量是不是真的在涨,而不是自我感觉良好。

一个实用技巧:把置信度分成三档处理。高置信度(比如 0.6 以上)的框默认保留,快速扫过;中档(0.3 到 0.6)重点复核;低档(0.3 以下)默认丢弃,但如果某类目标频繁出现在低档里,说明模型对该类信心不足,下一轮训练要针对性补这类样本。这个分档策略能把复核时间压到最低,同时不放过模型的薄弱环节。

还有一个容易被忽略的点:输入尺寸。很多人导出时用 640,但自己的图片分辨率很高,目标在缩放后变得很小,召回自然差。如果目标普遍偏小,可以试试导出 960 或 1280 的输入,代价是推理变慢,但预标注的召回会明显改善。这个权衡要在自己的数据上实测,没有通用答案。

我自己踩过最深的一个坑,是早期图省事,把预标注结果直接合并进训练集,没做复核。结果模型把上一版的漏检和误检当成了正确答案,第二轮反而更差。从那以后我养成了一个习惯:任何自动标注的输出,进训练集之前必须过一遍人工,哪怕只是快速扫。这个习惯看起来费时间,实际上省下了后面反复排查模型为什么退化的时间。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/12 2:25:57

真正的产品差异化,从来不是堆砌功能

装修时,很多人会把注意力放在家具、软装和墙面上,但真正决定空间质感的,往往是那些看似低调、却贯穿全屋的饰面板材料。 丰富品类、完整色系和明显的产品差异化,为不同户型、不同风格、不同生活需求提供更灵活的家装解决方案&…

作者头像 李华
网站建设 2026/10/12 2:25:55

小模型也能做数据分析智能体:把 harness 收窄做小

小模型也能做数据分析智能体:把 harness 收窄做小原文:NVIDIA Technical Blog - 《Building Reliable Data Analytics Agents: Lessons from the KDD Cup》(https://developer.nvidia.com/blog/building-reliable-data-analytics-agents-less…

作者头像 李华
网站建设 2026/10/12 2:25:40

Agent入门:让大模型“记住”并操作外部世界的秘密

Agent是围绕大语言模型构建的任务执行系统,通过记忆机制整合历史信息,利用工具调用和循环机制实现多步任务处理。本文详细解析了Agent如何让LLM“记住”对话,感知并操作外部世界,适合想要了解大模型进阶应用的开发者学习。 很多人…

作者头像 李华
网站建设 2026/10/12 2:25:23

机械臂自适应控制:空间神经网络与八叉树路径规划解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华