🧠 第四章 数据标注:引导模型学习提升性能
📄 一句话概括

把 car 改名成 automobile,要动 1 处标签定义 + 2 处框引用。 少改一处,那个框就引用了不存在的标签——训练时它会被当成背景,模型学成「这类目标不用管」。

📝 本节和前面几节的关系

上一节把框画完了,这一节处理一个「看着很小、后果很大」的操作:改名。 标签名和项目名都要改,但两者的风险完全不同。

一、改名为什么是个危险操作

标注文件里,同一个名字出现在两个地方:标签定义表(有哪些标签), 和每个框上的引用(这个框属于哪个标签)。 它们是两套数据,靠字符串对起来。改名要同时改两边,漏一边就断链。 断了之后不会报错——数据照样能读,训练照样能跑。 但那个框的类别找不到定义,多数框架会静默地把它当背景。 结果就是:你标了 40 个汽车框,其中 8 个因为改名漏了,模型学到的是「这 8 个位置什么都没有」。 它会把汽车学成背景。

二、两处引用,一个检查

Demo 里的数据很小:3 个标签、3 张图、5 个框。改名之后统计:改了 1 处标签定义、2 处框引用。 为什么框引用只有 2 处?因为 5 个框里只有 2 个是 car,其余是 person 和 traffic light。 这个数字必须跟改之前数出来的对上。 改完跑三项检查,一项都不能省: ① 引用都在定义里:框用到的每个标签,定义表里必须都有。 ② 没有残留旧名:全文搜旧名,命中次数必须是 0。 ③ 数量没变:改前 5 个框,改后还是 5 个。 这三项都是「不检查也不会报错」的类型,所以必须写成脚本自动跑。 手工改一次、人眼核一遍,是这套流程里最不可靠的一环。

三、项目名和标签名,风险不一样

标签名进数据,改错会影响训练——这是硬伤。 项目名不进数据,它只是这批标注的标题,导出时可能被当成文件名前缀。 它不影响模型,但影响交付。 所以风险等级要分开看:标签名改动要「改 + 查 + 复标抽检」;项目名改动只要「确认下游没按名字匹配」。 项目名改动的坑在自动化流程里:如果有脚本按「项目名_日期.json」这种规则去找文件, 改了名字就找不到了。改之前先搜一遍有没有人依赖它。 还有一条:改名之后,之前导出的文件要不要跟着改?答案是「不要」。 旧文件保持原样、加个说明,新文件用新名——批量改历史文件,是把一次风险放大成 N 次。

四、在 MakeSense 里改名

📋 实操清单(要在浏览器里点): 第一步,打开项目,左侧标签面板上右键(或点标签旁边的小图标),选重命名/编辑标签那一项,改完回车。 第二步,这个动作会自动更新所有已画的框——这是工具帮你做的一致性。 所以界面上改名比改文件安全得多。 第三步,改完立刻导出一份,用脚本跑那三项检查。不要只看界面上显示对了就放心。 🐍 沙箱等价物:三项检查的脚本见 Demo。 沙箱里点不了界面,但「引用和定义对不对得上」是纯数据结构问题,脚本一跑就知道。 而且脚本能处理工具处理不了的场景——比如合并两个项目、批量改历史数据。

五、动手 Demo:改名要动 1 处定义加 2 处引用,漏一处就断链

造一份小标注文件(3 个标签、3 张图、5 个框),把 car 改名成 automobile,统计改了几处; 再核对「定义表里的标签」和「框里用到的标签」是否一致。

# 两处引用都要改
for lb in data[u'labels']:
    if lb[u'name'] == OLD:
        lb[u'name'] = NEW

for im in data[u'images']:
    for bx in im[u'boxes']:
        if bx[u'label'] == OLD:
            bx[u'label'] = NEW

# 三项检查
names = set(lb[u'name'] for lb in data[u'labels'])
used = set(bx[u'label'] for im in data[u'images'] for bx in im[u'boxes'])
print(used - names)          # 应该为空

实跑输出:

--------------------------------------------------------------------
改标签名:改一处漏三处,模型就学错了
--------------------------------------------------------------------
  文件                     annotations.json
  项目名                   旧项目名
  标签                     3 个
  图                       3 张、5 个框

  需求:把标签 car 改名成 automobile。
  改了标签定义             1 处
  改了框上的引用           2 处
  标签定义里有             automobile、person、traffic light
  框里用到                 automobile、person、traffic light
  不一致的                 无

  **一次改名要动两处:标签定义,和每个框上的引用。**
  少改一处,就会得到一个「框引用了不存在的标签」的数据集——
  训练时这个框会被当成背景,**模型就学成「这类目标不用管」。**

  顺手把项目名也改掉(它不影响标注,但影响交付):
  新项目名                 新项目名·2026
  项目名在导出时经常被当成文件名前缀。**改之前先确认下游有没有按名字匹配。**

  改完必须跑一次一致性检查,三项都要过:
  ① 引用都在定义里         框用到的标签,定义里必须都有
  ② 没有残留旧名           全文搜旧名,命中 0 次
  ③ 数量没变               改前 5 个框,改后还是 5 个

结果:标签定义改了 1 处、框引用改了 2 处。 总共 3 处,都是字符串替换。 改完之后两个集合都对上了:定义里有 automobile、person、traffic light,框里用到的也是这三个, 差集为空。 这个「差集为空」就是第一项检查。 如果差集不为空,说明有框引用了不存在的标签——那一刻数据就已经坏了。 第二项检查(搜旧名命中 0 次)Demo 里没单独打印,因为改动是程序做的,天然不会有残留。 但手工改的时候这一项最容易漏,比如某个文件里还存着旧名的一份缓存。 第三项检查(框数不变)也值得单独跑: 改名只该改字符串,不该增减任何元素。 数量一变就说明处理逻辑写错了。

30 秒实验

  1. 只改标签定义、不改框引用,再跑一次三项检查,看差集里出现什么。 这就是「少改一处」的后果,一眼就能看见。
  2. 把两个标签合并成一个(比如 car 和 automobile 都要并成 vehicle),看需要改几处、 合并后每一类各有多少框。合并比改名更容易漏。
  3. 加一项检查:标签名里有没有空格和大写字母。改名是顺手把命名规范统一的最好时机。

常见误区

  1. 「改个名而已,编辑器全局替换一下就行」。 全局替换会改到不该改的地方(比如输出文本里恰好有这个词)。要按结构改,不按字符串改。
  2. 「界面上显示对了就没问题」。界面只显示一层。导出文件里有没有残留旧名,得跑脚本查。
  3. 「历史文件也一起改掉保持一致」。批量改历史文件是把一次风险放大成 N 次。 旧文件保持原样,新文件用新名。
自测三问(点开对答案) 1. 改名要改哪两处? —— 标签定义表里的名字,和每个框上记录的标签引用。它们是两套数据,靠字符串对齐,漏一边就断链。 2. 漏改会有什么后果?为什么不会报错? —— 那个框的类别找不到定义,多数框架会静默地把它当背景,模型学成「这类目标不用管」。 因为它不违反格式,所以不报错。 3. 改完要跑哪三项检查? —— 框用到的标签都在定义里、全文搜旧名命中 0 次、框的数量没变。 三项都是「不检查也不报错」的类型。

名字改好了,最后一步是把数据交出去。下一节讲导出,以及坐标格式的坑。

相关笔记