🏠 总目录📚 本教程 10 · 标注一致性
📑 本页目录(点开跳转)

10 · 标注一致性怎么量

76 分钟 | ⭐ 同意率 90% 的那批标注,Kappa 可能只有 0.20


🎯 一句话

一致性不是「两个人标得一样的比例」,是「他们比闭着眼睛乱按好了多少」。 上一章说「指南写不清楚,标注员就替你定义了问题」—— 这一章给你测出来的办法:一个数字,告诉你指南到底清不清楚。 而第一件要做的事,是把「同意率」这个人人都在用的指标扔掉。


🎲 一、为什么不能用同意率:一个 90% 对 0.20 的真实计算

某客服工单标注,两个人独立判断「这条是不是投诉」,一共 1,000 条:

B 标:非投诉 B 标:投诉 行合计
A 标:非投诉 883 50 933
A 标:投诉 50 17 67
列合计 933 67 1000
   同意率(两人标得一样的比例)
     Po = (883 + 17) / 1000 = 0.900        ← 90%!验收报告写得很好看

   但先看两个人各自标了多少条"投诉":
     A 标了 67 条    B 标了 67 条
     ⭐ 而两人【同时】认为是投诉的,只有 17 条

   也就是说:一共 117 条被至少一个人认为是投诉,
   两人达成共识的只有 17 条 —— 共识率 14.5%

偶然一致率 Pe —— 如果两人各自闭着眼睛、按各自的比例(都是 6.7% 标投诉)随机乱按:

   Pe = P(两人都随机标非投诉) + P(两人都随机标投诉)
      = 0.933 × 0.933 + 0.067 × 0.067
      = 0.870489 + 0.004489
      = 0.874978          ⭐ 乱按都能有 87.5% 的同意率!

   Cohen's Kappa = (Po − Pe) / (1 − Pe)
                 = (0.900 − 0.874978) / (1 − 0.874978)
                 = 0.025022 / 0.125022
                 = 0.2001              💀

💀 这就是「同意率 90%」这句话的真相: 那 90% 里面,87.5 个百分点是白送的 —— 来自「两人都说不是投诉」这件几乎不需要判断的事。 真正靠标注水平挣来的,只有 2.5 个百分点, 而理论上最多能挣 12.5 个百分点。2.5 / 12.5 = 0.20,这就是 Kappa。

拆开正负两类看,更直白:

指标 数值 含义
整体同意率 0.900 好看,但没信息量
负类专属一致率(都标"非投诉") 0.9464 简单,不需要判断
正类专属一致率(都标"投诉") 0.2537 💀 这才是真实水平

一句话记法同意率问的是「你们多常一致」,Kappa 问的是「你们比按同样比例乱按好了多少」。 类别越不平衡,「乱按」的基线越高,同意率就越没有意义 —— 95% 的样本都是负类时,两人各自乱按也能拿到 90% 的同意率。

import numpy as np

def cohen_kappa_2x2(a, b, c, d):
    """a=两人都标负, b=A负B正, c=A正B负, d=两人都标正"""
    n = a + b + c + d
    po = (a + d) / n
    p1 = (a + b) / n          # A 标负类的边际比例
    p2 = (a + c) / n          # B 标负类的边际比例
    pe = p1 * p2 + (1 - p1) * (1 - p2)          # ⭐ 偶然一致率
    kappa = (po - pe) / (1 - pe)
    return {"同意率Po": round(po, 4), "偶然一致Pe": round(pe, 6),
            "Kappa": round(kappa, 4),
            # ⭐ 不平衡数据必须同时看正类专属一致率,它才反映真实水平
            "正类专属一致率": round(2 * d / (2 * d + b + c), 4),
            "负类专属一致率": round(2 * a / (2 * a + b + c), 4)}

print(cohen_kappa_2x2(883, 50, 50, 17))
# {'同意率Po': 0.9, '偶然一致Pe': 0.874978, 'Kappa': 0.2001,
#  '正类专属一致率': 0.2537, '负类专属一致率': 0.9464}

# 用 sklearn 交叉验证同一个数字
from sklearn.metrics import cohen_kappa_score
y1 = np.array([0] * 883 + [0] * 50 + [1] * 50 + [1] * 17)
y2 = np.array([0] * 883 + [1] * 50 + [0] * 50 + [1] * 17)
print(round(float(cohen_kappa_score(y1, y2)), 4))     # 0.2001  ⭐ 完全一致

📊 二、四个指标,各管什么场景

指标 人数 允许缺失 标签类型 什么时候用它
Cohen's Kappa 恰好 2 ❌ 两人必须标同一批全部样本 名义 双标重叠的最常见场景
Fleiss' Kappa 多人(每样本固定 k 个评分者) ❌ 每条样本的评分数必须相同 名义 ⭐ 众包:不要求是同一批人,每条随机分给 5 个人也行
Krippendorff's α 任意 允许缺失、允许每条评分数不同 名义/有序/区间/比例 ⭐⭐ 最通用;真实标注平台的数据几乎总是不规整的
加权 Kappa 2 人 有序 1~5 分、严重度 P0~P3、风险等级 —— 必须用
   怎么选:三个问题就够
   ───────────────────────────────────────────────
   标签是有序的吗(分数 / 等级 / 严重度)?
     └─ 是 → 加权 Kappa(二次权重)或 Krippendorff α(ordinal)

   每条样本的标注人数一样吗?有没有缺失?
     ├─ 不一样 / 有缺失 → Krippendorff's α   ⭐ 别硬凑成 Fleiss
     └─ 一样,且恰好 2 人 → Cohen's Kappa
        一样,多于 2 人   → Fleiss' Kappa

   ⚠️ 常见错误:多人场景下两两算 Cohen 再取平均
      —— 这不等于 Fleiss,在人数不齐时会系统性偏高

Fleiss 和 Cohen 的关键区别不是人数,是「谁标的」: Cohen 要求固定的两个人标完全部样本(它会分别估计两人各自的偏好); Fleiss 只要求每条样本被 k 个人标,这 k 个人是谁无所谓。 众包平台的数据天然是 Fleiss 形态,用 Cohen 去套要么算不出来,要么算的是别的东西。


🔢 三、实跑:同一批数据,指标能差多少

① 指南清楚 vs 指南有歧义(Fleiss,20 个样本 × 3 类 × 5 人)

场景 观测一致 P̄ 偶然一致 Pe Fleiss' Kappa
指南清楚(多数样本 5 人全票或 4:1) 0.840 0.342 0.757
指南有歧义(多数样本 2:2:1 / 3:1:1) 0.255 0.349 −0.145 💀

💀 Kappa 可以是负数,而且负数有明确含义比随机还差。 这几乎从不是"标注员太笨",而是 一部分人在按某种系统性的反向理解在标 —— 比如把"是否包含广告"读成了"是否是好评"。 看到负 Kappa,第一件事是去看混淆矩阵有没有明显的对角线错位,不是去换人。

② 有序标签上,加不加权差 0.45(同一份 400 条 1~5 分打分数据,两人)

指标 数值 说明
同意率 0.5175 只有完全相同才算对
无权 Kappa 0.3964 💀 「差一档」和「差四档」被当成同样的错
线性加权 Kappa 0.6747 惩罚与差距成正比
二次加权 Kappa 0.8510 差一档几乎不罚,差三档罚很重

⚠️ 有序标签上用无权 Kappa,会把一份很好的标注判成"一般": 这份数据里两人绝大多数分歧只差 1 档 —— 对「情感强度 1~5 分」这类任务, 差 1 档基本无害,差 3 档才是真问题。 无权 Kappa 给 0.3964(看着该返工),二次加权给 0.8510(其实相当好)。 ⭐ 但反过来也要小心:二次加权容易把中等质量的标注美化成优秀, 报告里要写清楚用的是哪种权重 —— 只写「Kappa = 0.85」是一句没有信息量的话。

③ Krippendorff's α(12 个单元,每单元 2~3 人,人数不齐)

数据 α
大多数单元内部一致 0.7071
同样规模但标得很乱 −0.2247
import numpy as np
from collections import Counter
from sklearn.metrics import cohen_kappa_score

def fleiss_kappa(mat):
    """mat: N×k 矩阵,mat[i][j] = 第 i 条样本被标成第 j 类的人数
       ⚠️ 要求每行人数相同"""
    mat = np.asarray(mat, float)
    N, _ = mat.shape
    n_r = mat.sum(1)[0]                                  # 每条样本的评分人数
    p_j = mat.sum(0) / (N * n_r)                         # 各类别的整体占比
    P_i = (np.square(mat).sum(1) - n_r) / (n_r * (n_r - 1))
    P_bar, P_e = P_i.mean(), np.square(p_j).sum()
    return (P_bar - P_e) / (1 - P_e)                     # ⭐ 和 Cohen 同一个公式骨架

def krippendorff_nominal(units):
    """units: [[标注1, 标注2, ...], ...]  ⭐ 允许每个单元人数不同、允许缺失"""
    units = [u for u in units if len(u) >= 2]
    n_total = sum(len(u) for u in units)
    Do = 0.0
    for u in units:
        m, cnt = len(u), Counter(u)
        Do += (m * (m - 1) - sum(v * (v - 1) for v in cnt.values())) / (m - 1)
    Do /= n_total
    allc = Counter(v for u in units for v in u)
    De = (n_total ** 2 - sum(v * v for v in allc.values())) / (n_total - 1) / n_total
    return 1 - Do / De                                   # ⭐ 观测分歧 / 期望分歧

M = [[5,0,0],[4,1,0],[5,0,0],[0,5,0],[1,4,0],[0,4,1],[0,0,5],[0,1,4],[5,0,0],[4,0,1],
     [0,5,0],[0,5,0],[0,0,5],[1,0,4],[5,0,0],[0,4,1],[0,0,5],[4,1,0],[0,5,0],[5,0,0]]
print(round(fleiss_kappa(M), 3))                                   # 0.757

U = [["a","a","a"],["a","a"],["b","b","b"],["b","a"],["c","c"],["c","c","c"],
     ["a","a","b"],["b","b"],["c","b"],["a","a","a"],["b","b","b"],["c","c"]]
print(round(krippendorff_nominal(U), 4))                           # 0.7071

rng = np.random.default_rng(3)                                     # 有序标签演示
t = rng.integers(1, 6, 400)
r1 = np.clip(t + rng.choice([-1, 0, 0, 0, 1], 400), 1, 5)
r2 = np.clip(t + rng.choice([-1, 0, 0, 0, 1], 400), 1, 5)
for w in (None, "linear", "quadratic"):
    print(w, round(float(cohen_kappa_score(r1, r2, weights=w)), 4))
# None 0.3964 / linear 0.6747 / quadratic 0.851   ⭐ 有序标签必须加权

🛑 读到这里可以停 —— 前半章讲完了(约 26 分钟)。 后半章还有:阈值:多少算够 · Kappa 自己的三个坑 · 一致性低了怎么定位:看分歧矩阵,不看总分 · 事故复盘:评测集只有一个人标,结论被标反了 回来的时候不用重读,直接从下一节接着看就行。


📏 四、阈值:多少算够

学术上的经验分档(Landis & Koch):

Kappa 通常的说法
< 0 比随机还差 💀
0.00 ~ 0.20 几乎没有一致性
0.21 ~ 0.40 微弱
0.41 ~ 0.60 中等
0.61 ~ 0.80 较强
0.81 ~ 1.00 极强

但工程上真正要记的是按用途分的这一张

用途 建议下限 为什么
训练集(大规模、容忍噪声) ≥ 0.60 低于此,标签噪声会开始吃掉模型的收益(第 11 章)
评测集 ≥ 0.80 ⭐⭐ 评测集的一致性就是你能测出的性能上限
高风险场景(医疗 / 风控 / 内容安全) ≥ 0.80 且必须有仲裁 错一条的代价不对称
主观任务(创意质量、情感强度) 0.5 也可能是天花板 这时该改用加权 Kappa 或换成偏好对比

⭐⭐ 这一章最该记住的一句话人类之间的一致性,是模型性能的天花板。 两个人对同一批样本只有 0.65 的一致性,你不可能得出一个可信的「模型准确率 95%」的结论 —— 那 95% 里必然有一部分是在拟合标注噪声。 所以:先看评测集的一致性,再看模型在评测集上的分数。顺序反了,后面全是幻觉。

⚠️ 阈值必须按类别看,不能只看总体。 总体 0.72 完全可能是 「28 个类都在 0.85 以上,2 个类在 0.20 以下」—— 见第六节。


🪤 五、Kappa 自己的三个坑

① Kappa 悖论:同样的同意率,Kappa 能差 5 倍(实跑)

场景 混淆矩阵 Po Pe Kappa
类别均衡 [[425,75],[75,425]] 0.850 0.5000 0.700
类别不均衡 [[830,75],[75,20]] 0.850 0.8280 0.128 💀

⚠️ 同样的 Po = 0.850,Kappa 从 0.700 掉到 0.128 —— 分歧的绝对条数完全一样(都是 150 条)。 差别只在于类别的边际分布。这叫 prevalence effect(普遍性效应)。 ⭐ 所以在极不平衡的任务上,低 Kappa 不一定意味着标注差 —— 要把 Kappa + 正类专属一致率 + 分歧的绝对条数 三个一起报,只报一个数会误导。

② Bias effect:两人边际分布差很多时,Kappa 可能反而虚高。 Kappa 把「两人各自的标注倾向」吸收进了 Pe。 一个人标 30% 正类、另一个标 10% 正类时,Pe 被压低,Kappa 会显得比直觉好 —— 所以报 Kappa 时一定要同时报两人各自的类别分布

③ Kappa 测的是「一致」,不是「对」。

   两个标注员都把"含联系方式的好评"标成【普通评价】
     → Kappa = 1.00   完美一致
     → 但按指南,它们全都该是【广告】

   💀 一致性高只说明【指南被一致地理解了】,
      不说明【被正确地理解了】—— 集体误读的 Kappa 也是 1.0
   ⭐ 所以一致性(双标重叠)和正确性(黄金集)是两套独立的质检,
      第 9 章那三种手段,一个都不能省。

🔍 六、一致性低了怎么定位:看分歧矩阵,不看总分

一个总分是没法行动的。要看分歧集中在哪一对类别上。

实跑:600 条工单,4 个类(咨询 / 投诉 / 退款 / 其他),两名标注员。

   整体同意率 = 0.7683      整体 Cohen's Kappa = 0.6477

   混淆矩阵(行 = A,列 = B)
                咨询   投诉   退款   其他
        咨询    282      0      0     51
        投诉      0     22     46      0
        退款      0     42     65      0
        其他      0      0      0     92

   按【类别对】拆解全部 139 条分歧:
     投诉 ↔ 退款    88 条   占全部分歧 63.3%   ⭐
     咨询 ↔ 其他    51 条   占全部分歧 36.7%
     其余 4 对       0 条   占全部分歧  0.0%

这个 0.6477 不是「整体质量中等」,是「两对类别的定义坏了,其余六对完美」。 把「投诉 / 退款」合并成一类之后,同一份数据的 Kappa 直接变成 0.8624一个中等的总分,几乎总是少数几对类别拖的 —— 修那两对,比培训所有人有用一百倍。

⚠️ 但「合并类别」只是诊断手段,不是解决方案。 合并之后 Kappa 大涨,说明的是:在标注员眼里这两类本来就是一类。 接下来你只有两个选择 —— 要么改指南把边界写死(第 9 章的边界样例),要么真的合并它们。 留着一对标注员分不开的类别,模型也一定分不开。

import numpy as np
from sklearn.metrics import cohen_kappa_score, confusion_matrix

def disagreement_profile(a, b, labels):
    """⭐ 一致性诊断的正确起点:不是总分,是按类别对拆解的分歧"""
    a, b = np.asarray(a), np.asarray(b)
    cm = confusion_matrix(a, b, labels=list(range(len(labels))))
    pairs = []
    for i in range(len(labels)):
        for j in range(i + 1, len(labels)):
            n = int(cm[i, j] + cm[j, i])
            if n:
                pairs.append((n, labels[i], labels[j]))
    pairs.sort(reverse=True)
    total = sum(p[0] for p in pairs) or 1
    return {"同意率": round(float((a == b).mean()), 4),
            "Kappa": round(float(cohen_kappa_score(a, b)), 4),
            "分歧总数": total,
            # ⭐ 只要 Top-2 类别对占了 >60% 的分歧,就先修这两对,别做全员培训
            "类别对": [(x, y, n, f"{n/total:.1%}") for n, x, y in pairs]}

def kappa_by_class(a, b, labels):
    """⚠️ 总体 Kappa 会把个别坏类别平均掉:逐类做 one-vs-rest"""
    a, b = np.asarray(a), np.asarray(b)
    return {labels[k]: round(float(cohen_kappa_score(a == k, b == k)), 4)
            for k in range(len(labels))}

💀 七、事故复盘:评测集只有一个人标,结论被标反了

发生了什么

   系统:短视频平台的「不适宜内容」自动审核模型
   评测集:8,000 条,⚠️ 单人标注("为了省钱、也为了口径统一")
          从来没有算过一致性 —— 团队认为"评测集不用算一致性"
   决策:候选模型 B  F1 = 0.812   现网模型 A  F1 = 0.774
        差 0.038,判定为显著提升 → 全量替换

上线五周后,人工复审队列的申诉量涨了 2.8 倍。复盘时才第一次去测评测集本身:

   抽 1,200 条评测集样本,3 名资深审核员重标 + 1 名仲裁

     Fleiss' Kappa(全部类别)        0.38   💀
     「擦边内容」这一类的 Kappa       0.19   💀💀

     原评测集那位标注员判为违规的比例   11.2%
     三人仲裁后的违规比例               6.7%
     ⭐ 他系统性地比团队均值宽松了 4.5 个百分点

   而模型 B 恰好也倾向宽松 —— 因为它的训练数据也大量来自同一个人。
   ⭐⭐ 于是这场评测测的根本不是"哪个模型更准",
        而是"哪个模型更像那位标注员"。

   用仲裁后的金标重新评测:
     模型 A  F1 = 0.781        模型 B  F1 = 0.729
     💀 结论完全翻转

为什么没被发现(三条)

   ① "评测集不用算一致性"是个流行的误解
      理由听起来很合理:"一个人标口径最统一"
      💀 口径统一 ≠ 口径正确。一个人标 = 把一个人的偏好固化成了金标准 ⭐

   ② 0.038 的差距被当成了显著提升,没做任何区间估计
      实测:8,000 条评测集、正样本 546 条时,
      单个模型 F1 的 bootstrap 95% 区间半宽就有 ±0.027
      ⭐ 0.038 只比它略大 —— 这种量级的差距必须做配对显著性检验,不能直接拍板

   ③ 灰度期只看模型侧指标(拦截量、拦截率),没有看申诉率
      拦截量确实涨了,看起来像"新模型更能抓"
      💀 而误伤和抓得准,在拦截量这一个数字上长得一模一样

代价

   全量运行 5 周后回滚

     误伤率(正常内容被下架)  0.31%  →  0.94%
     被误伤内容                4.1 万条
     创作者申诉                1.7 万件
     2 名头部创作者公开投诉

   直接成本:
     回滚 + 人工复审 4.1 万条              ≈ 21 万元
     重建评测集(3 人 × 3 周 + 外部标注)  ≈ 41 万元
     ⭐ 合计约 62 万元,新模型上线推迟 9 周

💀 这个事故的本质评测集只有一个标注员时,你评测的是「模型有多像这个人」,不是「模型有多准」。 而这件事在离线指标上永远看不出来 —— 因为分母和分子用的是同一个人的口径。 上一章那个 41 万的事故是训练集的歧义,这一章是评测集的歧义; 后者更贵,因为它会让你自信地做出一个反向的决策。

该补什么

缺失 补上之后
评测集单人标注 评测集必须多人标 + 仲裁,并且先报一致性再报模型分数 ⭐⭐
从不测一致性 评测集 Fleiss/Krippendorff < 0.80 不许用于选型决策;按类别单独看
只看总体 Kappa 逐类 one-vs-rest:「擦边」0.19 这个数字本可以在第一天就拦住整件事 ⭐
模型分数没有区间 F1/AUC 一律带 bootstrap 置信区间;模型对比用配对检验,不比点估计
灰度只看模型侧指标 人侧指标:申诉率、复审改判率 —— 误伤和抓得准在拦截量上长得一样 ⭐
训练集和评测集同一批人标 评测集的标注员必须和训练集的不重叠,否则模型学到的偏好会被当成"正确"

最后一条最容易被当成吹毛求疵,但它正是这个事故的引信: 模型 B 在训练时学会了那位标注员的宽松倾向, 评测时又被同一位标注员的标准打分 —— 它两头都在讨好同一个人。 只要训练集和评测集共用标注员,这种自我印证就无法被离线指标发现。


🔗 这一章连到哪里

去哪 为什么
第 9 章 · 标注体系怎么设计 一致性低了要回到指南去修:边界样例、排除项、试标注循环
第 11 章 · 标签噪声 Kappa 低意味着标签噪声高;那一章讲噪声对模型的量化影响和处理办法
第 13 章 · 评测集也是数据 本章事故的正面写法:评测集该怎么建、为什么标准比训练集更高
第 8 章 · 数据质量怎么量 一致性是「准确性维度」的人工那一半,同样要进质量报告、要看趋势
上线之后 12 · AB实验你以为你懂了 「0.038 算不算显著」是同一类问题:点估计不带区间就做决策
大模型全景导论 11 · 评测体系 LLM-as-judge 同样要测一致性:判官和人的 Kappa 才是这套评测的上限

✅ 检查点

  1. 那个 1,000 条的例子里,同意率 0.900 而 Kappa 只有 0.2001 —— Pe 是多少?这 90% 里有多少个百分点是"白送的"?
  2. 为什么正类专属一致率(0.2537)比整体同意率更能反映真实水平?
  3. Cohen's / Fleiss' / Krippendorff's α / 加权 Kappa 各自的适用场景是什么?Cohen 和 Fleiss 最关键的区别是什么(不是人数)?
  4. 多人场景下「两两算 Cohen 再平均」为什么不对?
  5. Fleiss Kappa 算出 −0.145 意味着什么?第一件该做的事是什么?
  6. 同一份 1~5 分打分数据上,无权 / 线性 / 二次加权 Kappa 分别是多少?为什么有序标签必须加权?加权又有什么风险?
  7. 训练集、评测集、高风险场景的 Kappa 下限建议分别是多少?「人类一致性是模型性能天花板」是什么意思?
  8. Kappa 悖论是什么?两个 Po 都是 0.850 的例子,Kappa 分别是多少?这时候该怎么报数?
  9. 两个标注员一致地把广告标成普通评价,Kappa 是多少?这说明一致性和什么是两回事?
  10. 那个 600 条 4 类的例子里,Kappa 0.6477 的分歧是怎么分布的?合并两类之后变成多少?为什么"合并"不是解决方案?
  11. 那个 62 万的事故里,评测集单人标注具体错在哪?为什么离线指标永远看不出来?
  12. 事故里 0.038 的 F1 差距为什么不能直接拍板?实测的 bootstrap 区间半宽是多少?
👀 答案
  1. Pe = 0.874978。因为两人各自只有 6.7% 标"投诉",闭眼乱按都能拿到 87.5% 的同意率。所以 90% 里 87.5 个百分点是白送的,靠标注水平挣来的只有 2.5 个百分点(最多能挣 12.5 个),2.5/12.5 = 0.2001 就是 Kappa。
  2. 因为整体同意率里 87.5 个百分点的贡献来自"两人都标非投诉"这件几乎不需要判断的事。两人各标了 67 条投诉,共同认定的只有 17 条;正类专属一致率 0.2537 vs 负类 0.9464 —— 需要判断的那一半,一致性其实很差。
  3. Cohen:恰好 2 人、且两人标同一批全部样本、名义标签。Fleiss:多人、每条样本被固定 k 人标、名义标签。Krippendorff α:任意人数、允许缺失和每条评分数不同、支持名义/有序/区间/比例,最通用。加权 Kappa:有序标签必须用。Cohen 和 Fleiss 的关键区别不是人数而是「谁标的」 —— Cohen 要求固定的两个人(会分别估计两人各自的偏好),Fleiss 只要求每条被 k 个人标、是谁无所谓,众包数据天然是 Fleiss 形态
  4. 因为它不等于 Fleiss,在人数不齐时会系统性偏高。数据不规整时正确做法是用 Krippendorff's α,别硬凑成 Fleiss 或平均 Cohen。
  5. 比随机还差。这几乎从不是"标注员太笨",而是一部分人在按某种系统性的反向理解在标(比如把"是否含广告"读成了"是否是好评")。第一件事是看混淆矩阵有没有明显的对角线错位,不是换人。
  6. 同意率 0.5175,无权 0.3964 / 线性 0.6747 / 二次加权 0.8510。必须加权是因为无权 Kappa 把「差一档」和「差四档」当成同样的错,而这份数据的分歧绝大多数只差 1 档 —— 对情感强度这类任务差 1 档基本无害。风险是二次加权容易把中等质量美化成优秀,所以报告必须写清用的是哪种权重,只写"Kappa=0.85"没有信息量。
  7. 训练集 ≥0.60评测集 ≥0.80,高风险场景(医疗/风控/内容安全)≥0.80 且必须有仲裁;主观任务 0.5 可能就是天花板,该换加权 Kappa 或偏好对比。「人类一致性是天花板」意思是:两人只有 0.65 的一致性时,不可能得出可信的"模型准确率 95%"结论 —— 那里面必有一部分在拟合标注噪声。所以先看评测集一致性,再看模型分数
  8. Kappa 悖论(prevalence effect):同样的 Po、同样的分歧绝对条数(都是 150 条),仅仅因为边际分布不同,Kappa 从 0.700(均衡 [[425,75],[75,425]])掉到 0.128(不均衡 [[830,75],[75,20]]。所以极不平衡任务上低 Kappa 不一定意味着标注差,要把 Kappa + 正类专属一致率 + 分歧绝对条数三个一起报。
  9. Kappa = 1.00(完美一致),但按指南它们全都该标成广告。说明一致性高只说明指南被「一致地」理解了,不说明被「正确地」理解了 —— 集体误读的 Kappa 也是 1.0。所以一致性(双标重叠)和正确性(黄金集)是两套独立的质检,一个都不能省。
  10. 全部 139 条分歧里:投诉↔退款 88 条(63.3%)、咨询↔其他 51 条(36.7%)、其余 4 对 0 条。把投诉/退款合并后 Kappa 从 0.6477 变成 0.8624。合并不是解决方案是因为:Kappa 大涨说明的是在标注员眼里这两类本来就是一类,接下来只有两条路 —— 改指南把边界写死,或者真的合并;留着一对标注员分不开的类别,模型也一定分不开
  11. 评测集 8,000 条单人标注、从没算过一致性(理由是"一个人标口径最统一")。💀 口径统一 ≠ 口径正确 —— 一个人标等于把一个人的偏好固化成了金标准。事后 3 人重标:整体 Fleiss 0.38,「擦边」类只有 0.19;那位标注员判违规的比例 11.2% vs 仲裁后 6.7%,系统性宽松 4.5pp;而模型 B 的训练数据也大量来自他,所以 B 只是"更像那个人"。用仲裁金标重测:A 0.781 vs B 0.729,结论完全翻转。离线看不出来是因为分子和分母用的是同一个人的口径。代价:误伤率 0.31%→0.94%,误伤 4.1 万条、申诉 1.7 万件,约 62 万元,上线推迟 9 周
  12. 因为这种量级的差距必须做配对显著性检验。实测:8,000 条评测集、正样本 546 条时,单个模型 F1 的 bootstrap 95% 区间半宽就有 ±0.027,0.038 只比它略大。另外灰度只看拦截量也不行 —— 误伤和抓得准在拦截量这一个数字上长得一模一样,必须补申诉率、复审改判率这类人侧指标。

🛑 可以停在这里

走神救援

⭐⭐一致性不是「两人标得一样的比例」,是「他们比按同样比例乱按好了多少」。 构造例子(1000 条,两人判是否投诉,矩阵 [[883,50],[50,17]]):同意率 Po = 0.900,看着很棒;但 Pe = 0.874978 —— 闭眼乱按都能有 87.5%,所以 Kappa = (0.900−0.874978)/(1−0.874978) = 0.2001。💀 那 90% 里 87.5 个百分点是白送的,靠水平挣的只有 2.5 个(最多能挣 12.5 个)。两人各标了 67 条投诉,共同认定的只有 17 条正类专属一致率 0.2537 vs 负类 0.9464 —— 需要判断的那一半其实很差。类别越不平衡,同意率越没意义。 四个指标:Cohen(恰好 2 人、标同一批全部样本)、Fleiss(多人、每条固定 k 人、⭐不要求是同一批人,众包数据天然是这个形态)、Krippendorff α(⭐⭐任意人数、允许缺失和评分数不齐、支持有序/区间,最通用)、加权 Kappa(有序标签必须用);⚠️ 多人场景两两算 Cohen 再平均不等于 Fleiss,人数不齐时会系统性偏高。实跑对比:指南清楚 Fleiss 0.757,指南有歧义 −0.145(💀负数 = 比随机还差,多半是一部分人在系统性反向理解,先查混淆矩阵的对角线错位,别急着换人);同一份 1~5 分数据 无权 0.3964 / 线性 0.6747 / 二次 0.8510 —— ⚠️有序标签用无权 Kappa 会把好标注判成要返工(分歧几乎都只差 1 档),但二次加权又容易把中等美化成优秀,所以报告必须写清权重;Krippendorff α 实跑 0.7071 vs 乱标 −0.2247。阈值:训练集 ≥0.60、评测集 ≥0.80、高风险场景 ≥0.80 且必须仲裁,主观任务 0.5 可能就是天花板。⭐⭐人类一致性是模型性能的天花板 —— 两人只有 0.65 的一致,"模型准确率 95%"这个结论必然有一部分在拟合标注噪声,所以先报评测集一致性,再报模型分数。Kappa 三个坑:①Kappa 悖论 —— 实跑两个 Po 都是 0.850、分歧都是 150 条,均衡时 Kappa 0.700,不均衡时只有 0.128,所以不平衡任务要把 Kappa + 正类专属一致率 + 分歧绝对条数一起报;②bias effect,两人边际分布差很大时 Kappa 反而虚高,要同时报两人各自的类别分布;③💀Kappa 测「一致」不测「对」 —— 两人一致地把广告标成普通评价,Kappa = 1.0,所以一致性(双标重叠)和正确性(黄金集)是两套独立质检。定位靠分歧矩阵不靠总分:实跑 600 条 4 类,Kappa 0.6477,全部 139 条分歧里投诉↔退款 88 条(63.3%)、咨询↔其他 51 条(36.7%)、其余四对 0 条,合并投诉/退款后 Kappa 变 0.8624 —— ⭐中等的总分几乎总是少数几对类别拖的,修那两对比全员培训有用一百倍;⚠️但合并只是诊断不是解药,它说明这两类在标注员眼里本来就是一类,接下来要么改指南写死边界要么真合并,留着一对人分不开的类别,模型也一定分不开。💀事故:短视频审核模型选型,评测集 8,000 条单人标注、从没算过一致性("一个人标口径最统一");B 的 F1 0.812 vs A 0.774,差 0.038 就全量替换。五周后申诉量涨 2.8 倍才去测评测集:Fleiss 只有 0.38,「擦边」类 0.19;那位标注员判违规 11.2% vs 三人仲裁 6.7%,系统性宽松 4.5pp,而 B 的训练数据也大量来自他 —— ⭐⭐这场评测测的是"哪个模型更像那个人"。用仲裁金标重测 A 0.781 vs B 0.729,结论完全翻转。代价:误伤率 0.31%→0.94%、误伤 4.1 万条、申诉 1.7 万件、约 62 万元、上线推迟 9 周。三个盲区:"评测集不用算一致性"是流行误解(口径统一 ≠ 口径正确)0.038 没做区间估计(实测 8,000 条、546 个正样本时单模型 F1 的 bootstrap 95% 半宽就有 ±0.027,必须做配对检验)、灰度只看拦截量(误伤和抓得准在这个数字上长得一模一样,要补申诉率和复审改判率)。最狠的一条改法:评测集的标注员必须和训练集不重叠,否则模型两头讨好同一个人,离线指标永远发现不了。

下一节 👉 11-标签噪声.md

打卡记录保存在你的浏览器里,首页能看到总进度