大规模数据集上的自适应标注覆盖
Potato 2.6 允许你给大多数数据分配一位标注者、给一个分层样本分配三位,在标注者出现分歧的数据上提高覆盖度,并把争议数据路由给裁定者。
任何有一定规模的标注项目都存在一个固有的矛盾。如果每条数据都由两三位标注者处理,你可以测量一致性、信任自己的标签,但预算也随之翻了两三倍。如果每条数据只由一位标注者处理,同样的钱能标出三倍的数据,可你完全不知道其中任何一条有多可靠。
常见的折中办法,做过研究的人都很熟悉:语料的大部分单人标注,另取一小部分样本做双人或三人标注以监控质量。难点在于让工具干净利落地做到这一点,以及在拿到重叠标注之后真的用起来。Potato 2.6 把这套设计内建了进去,通过两个配置块(num_annotators_per_item 和 per_annotator_quota)加上自适应提升与裁定路由来实现。完整参考见异构覆盖文档。
Potato 中的自适应标注覆盖
逐条数据的上限与重叠样本
num_annotators_per_item 接受一个整数表示统一上限,也接受一个结构化映射,用于让不同数据获得不同覆盖度。最常见的形态是默认为 1,另取一个分层样本提高到 3:
num_annotators_per_item:
default: 1
overlap_sample:
fraction: 0.1
count: 3
stratify_by: domain
seed: 42
min: 1overlap_sample 块会在一个确定性的数据子集上提高上限。抽样在启动时进行一次,被选中的数据会在内部打上标记,此后分配逻辑就把它们当作高覆盖数据处理。fraction 是抽样比例,count 是提高后的上限(必须大于默认值),seed 让这个选择在重启之间可复现。
stratify_by 改变了比例的施加方式。把它指向数据中的某个字段(这里是 domain),比例就会按层施加,而不是在整个数据池上施加。每个类别都会按其规模比例地进入重叠样本,因此你不会在一个恰好 90% 来自单一领域的样本上测量一致性。如果你的语料混合了新闻、社交媒体和临床文本,每一类都会按其占比出现在质量样本中。
自适应提升
固定的重叠样本是盲选的,在任何人开始标注之前就已确定。但最需要第二、第三次审视的,恰恰是标注者真的出现分歧的那些数据,而你只有在第一轮之后才知道它们是哪些。自适应提升会在事后提高覆盖度:
num_annotators_per_item:
default: 1
adaptive:
enabled: true
disagreement_threshold: 0.5
boost_to: 3当一条数据至少已有两份标注、且其分歧分数越过 disagreement_threshold 时,它的上限会被提高到 boost_to,并重新进入分配队列等待下一轮。提升对每条数据只触发一次,因此有争议的数据只会被升级一次,不会无限膨胀。
逐标注者配额
覆盖上限控制的是每条数据由多少标注者处理。另有一个配置块控制每位标注者处理多少条数据,你通常会希望它按专业水平或合同条款而变化:
per_annotator_quota:
default: 100
by_user:
alice: 30
by_user_role:
expert: 30
novice: 200
user_roles:
alice: expert
carol: novice解析顺序是从最具体到最一般:先 by_user[uid],再 by_user_role[user_roles[uid]],最后 default。因此你可以把某位专家单独限制在 30 条、其余专家按角色同样限制在 30 条、新手限制在 200 条,而这套机制不会与上面的逐条数据上限相互干扰。
把重叠变成决策
只有当你真的处理这些分歧时,收集重叠标注才有意义。启用裁定配置块后,重叠样本中达到覆盖上限的数据会被自动打分,一致性低于阈值时就推入裁定队列:
adjudication:
enabled: true
adjudicator_users: [admin]
min_annotations: 2
agreement_threshold: 0.75低一致性的数据会在样本饱和的那一刻浮现出来,而不必等谁想起来手工重建队列。裁定者打开队列,看到的就是真正有争议的数据,那些大家都同意的部分已经被筛掉了。
解读一致性
重叠样本饱和之后,一致性统计可以在 /admin/iaa 查看。这个端点会针对每种方案类型计算相应的指标,而不是用一个数字套用到所有情况:名义型方案用 Cohen's 与 Fleiss' kappa,有序型用加权 kappa,区间型用 token 级 kappa 加上区间 F1。这一点很重要,因为把有序的李克特评分当作无序类别去算 κ,会低估真实的一致性。
试一试
版本中附带了一个可直接运行的演示。在仓库根目录下执行:
python potato/flask_server.py start examples/advanced/heterogeneous-coverage/config.yaml -p 8000它使用横跨两个领域的 20 条数据,按领域分层抽取 20% 做三人重叠标注,以 0.5 为阈值启用自适应提升,定义两个专业等级,并把低一致性的数据路由进裁定流程。
把它们串起来
上面这些部件让你可以决定标注预算花在哪里,而不是平均摊开。多数数据走一遍。一个分层切片走三遍,这样你能报告整个语料的可靠性,而不只是其中一角。事后发现困难的数据会被自动升级,有争议的则路由给裁定者。每一个覆盖决策背后都有一行配置,而这正是你写方法部分时需要的东西。
某个任务究竟需要多少标注者是另一个问题;需要多少标注者这篇文章梳理了其中的经验法则。异构覆盖随 Potato 2.6 发布;这些配置块的全部能力见异构覆盖文档和任务分配参考。