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

16 个标签、1255 个框,查出四个毛病:car 和 Car 分裂成两类; 8 个长尾标签只占 4.4% 的框;「汽车」和「车」字面相似度只有 0.67,却是同一个东西。 标签体系是契约,不是随手敲的词。

📝 本节和前面几节的关系

上一节定了任务类型,这一节定「标什么」。标签名敲错一个字,后面所有环节都要返工。

一、标签体系是一份契约

标签列表看起来只是个词表,实际上它规定了三件事:哪些东西要标、同一个东西叫什么、边界在哪里。 这三件事只要有一条不清楚,两个标注员就会标出两套数据。 而模型拿到的是这两套混在一起的结果——它会学到一个混乱的边界。 所以标签体系要满足四条:互斥(一个目标只能属于一类)、穷尽(该标的都有类可归)、 同层(「汽车」和「货车」不该并列)、可判定(两个人看同一张图能得出同样结论)。 四条里最容易破的是第四条。「什么算行人」——远处三像素的人算不算?被遮住一半的算不算? 照片里的海报上的人算不算?这些必须写进手册,不能靠「你懂的」。

二、四个毛病,能自动查的自动查

毛病一:大小写和拼写变体。 Demo 里 car 和 Car 同时存在,合并前 16 类、合并后 15 类。 模型会把它们当成两个完全不同的类,各学一半样本。 毛病二:同义标签。 「汽车」和「车」、「货车」和「卡车」、「行人」和「人」。 字面不同,意思一样。 麻烦在于字面相似度低的恰恰最危险:「汽车」和「车」只有 0.67,「摩托」和「摩托车」有 0.80。 按相似度排序,最该合并的排在后面。所以这一类只能靠人看,机器查不准。 毛病三:层级重叠。 「汽车」包含「货车」,它们就不该是并列标签。 查法是问一句「X 是不是一种 Y」,是就冲突。 毛病四:长尾标签。 Demo 里 16 个标签有 8 个出现不到 20 次,合计只有 55 个框、占 4.4%。 这些标签的标注成本一样花,对模型的贡献接近于零。

三、长尾标签怎么处理

两个办法,没有第三个:合并进父类,或者删掉。 「轿车」12 个框、「车」8 个、「car」3 个——这些全部并进「汽车」,从 4 个类变成 1 个类, 样本从 23 个变成 443 个。 「路面」只有 2 个框,而且它跟「交通标志」在语义上没什么关系——直接删掉, 或者归到一个「其他」类里,且不参与评估。 判断标准很简单:这个类如果只有十几个样本,模型学不会;学不会的类,在评估里只会拉低总分。 留着它唯一的用处是让报告难看。 有一个例外:如果这个类业务上极重要(比如「安全帽未佩戴」),样本少也要留, 但要做定向补采——去现场多拍,而不是靠现有数据硬撑。

四、在 MakeSense 里建标签

📋 实操清单(要在浏览器里点,沙箱里做不了): 第一步,打开 makesense.ai,点 Get Started,把图片拖进页面。 第二步,左侧面板就是标签区。 点「Create new label」逐个添加,每个标签会自动分配一个数字快捷键(1 到 9,再往上是 0 和字母)。 第三步,标签名一律用英文小写加下划线(traffic_light 而不是 Traffic Light)。 理由是导出到 YOLO 等格式时,中文和空格经常出问题。显示用中文可以另设,但键名要干净。 第四步,同一批数据里标签顺序定了就别改。 因为 YOLO 导出的是类别序号,不是类别名——顺序一变,之前导出的数据和之后的就对不上。 🐍 沙箱等价物:标签表的四个毛病用代码查,见 Demo。 其中大小写合并和长尾统计是纯字符串和计数,这两项能全自动查出来,一个脚本跑一遍就够。

五、动手 Demo:16 个标签里 8 个是长尾,合计只占 4.4% 的框

造一份 16 个标签、16 张图累计 1255 个框的标注统计,逐个查四类毛病:大小写变体、同义标签、 层级重叠、长尾标签。大小写和长尾两项用代码自动查,同义和层级靠人工判断。

# 毛病一:大小写/拼写变体(自动查)
var = {}
for t in raw:
    var.setdefault(t.lower(), []).append(t)
bad = [(k, v) for k, v in var.items() if len(v) > 1]

# 毛病四:长尾标签(自动查)
rows = []
for t in raw:
    if use[t] < 20:
        rows.append([t, u'%d' % use[t]])

# 毛病二:同义标签(只能人看,机器给个参考值)
difflib.SequenceMatcher(None, u'汽车', u'车').ratio()

实跑输出:

--------------------------------------------------------------------
标签体系:四个毛病,全都能自动查出来
--------------------------------------------------------------------
  标签总数                 16 个
  标注总数                 1255 个框

  第一个毛病:大小写和拼写变体。
    car → car/Car
  **同一个东西写成两种,训练时会被当成两类。** 合并前 16 类,合并后 15 类。

  第二个毛病:同义标签(字面不同、意思一样)。
    汽车 与 轿车,字面相似度 0.50
    汽车 与 车,字面相似度 0.67
    货车 与 卡车,字面相似度 0.50
    行人 与 人,字面相似度 0.67
    摩托 与 摩托车,字面相似度 0.80
  **字面相似度低的恰恰最危险**——「汽车」和「车」只有 0.67,但它们是同一个东西。**
  这类问题只能靠人看,机器查不准。**

  第三个毛病:层级重叠。
  「汽车」包含「货车」吗   包含。那它们就不该是并列标签
  怎么查                   问一句「X 是不是一种 Y」,是就层级冲突
  同类问题还有「交通标志」和「红绿灯」——红绿灯算不算标志?
  **必须二选一写进标注手册,否则两个人会标成两样。**

  第四个毛病:长尾标签。
  标签     出现次数
  ----------------
  轿车           12
  车              8
  car             3
  Car             2
  卡车            9
  人             15
  person          4
  路面            2
  16 个标签里有 8 个出现不到 20 次,合计只有 55 个框——占全部的 4.4%。
  **这 8 个标签的标注成本一样花,但对模型的贡献接近于零。**
  处理办法只有两个:合并进父类,或者干脆删掉。**别留着。**

第一段输出很直接:car 和 Car 被当成两个标签。合并前 16 类、合并后 15 类。 这一类错误一个 .lower() 就能查出来。 第二段是相似度参考值:「汽车」对「车」0.67、「摩托」对「摩托车」0.80。 最该合并的那一对,相似度反而最低——所以这一项不能按阈值自动合并,只能当提示,人来定。 第三段是长尾: 8 个标签出现不到 20 次,合计 55 个框,占全部的 4.4%。这个 4.4% 是最有说服力的数—— 八成标签只贡献了不到二十分之一的样本。 那句「标注成本一样花,对模型的贡献接近于零」不是夸张。 每个标签都要定义、要写手册、要在培训里讲一遍,这些固定成本跟样本量无关,全都摊在那 4.4% 上了。 要说明一点:这里的次数分布是我按真实数据集的样子设的,不是从某个公开数据集抓的。 要学的是查的方法和判断标准,不是这几个数。

30 秒实验

  1. 把 car 和 Car 合并之后重算总类数,再算每一类的框数占比。 你会看到「汽车」从 420 变成 425,而「Car」这一类的独立意义消失了。
  2. 把长尾里 5 个标签合并进父类,再算一遍长尾数量。 从 8 个降到 3 个,剩余长尾的框数占比从 4.4% 降到 1% 以下。
  3. 加一条自动检查:标签名里有没有空格、大写、中划线。这三样在导出到 YOLO 或 CSV 时都会出问题。 一条正则就能拦住。

常见误区

  1. 「标签名字随便写,反正导出时会转」。YOLO 导出的是类别序号,顺序一变,之前的数据就对不上。 键名要干净,顺序要固定。
  2. 「多余的小标签留着以后可能有用」。样本不到二十个的类,模型学不会,还会拉低整体评估。 要么合并,要么删。
  3. 「同义标签可以按相似度自动合并」。最该合并的那一对相似度往往最低(「汽车」和「车」只有 0.67)。 这一项必须人工判断。
自测三问(点开对答案) 1. 标签体系要满足哪四条? —— 互斥(一个目标只属一类)、穷尽(该标的都有类)、同层(父子不并列)、 可判定(两个人看同一张图能得出同样结论)。 2. 为什么长尾标签要合并或删掉? —— 样本太少的类模型学不会,学不会就会在评估里拉低总分; 而它的定义、手册、培训这些固定成本照样要花。 3. 为什么标签名建议用英文小写加下划线? —— 因为导出到 YOLO、CSV 等格式时,中文和空格经常出问题。 显示名可以是中文,但键名要干净、可跨工具。

标签定好了,可以真开始画了。下一节看「画得准不准」怎么量化。

相关笔记