跳转到内容

在标注数据上训练模型

把多位标注者的标签合并成训练目标,让留出的 test 划分在各轮之间保持固定,记录每个模型见过什么,并估算第一次运行所需的数据量。

要在标注数据上训练模型,就把每个条目的标签合并为一个目标或标注者的投票分布,用一条在各轮之间保持固定的规则把条目分配到 train、验证和 test 划分,并在验证划分选出了检查点时报告 test 得分。Potato 的模型训练在研究自己的标注上执行每一步,并记录每次运行训练过哪些条目。

v2.14.0 新增

标注数据很少恰好是训练器所读取的形状。有重叠的研究,每个条目有多个标签,而这些标签往往不一致。仍在进行中的研究每天都在增加标签,每一轮重新训练看到的条目都比上一轮更多。训练器需要每个条目一个目标,以及一组固定的条目来评分,下面的这些决定正好提供这两样东西。下面的每一节都会写明做出该决定的 Potato 设置。关于训练页面本身,请参阅模型训练概览,以及训练第一个模型的教程。如果想在自己的代码中训练,导出标注用于机器学习介绍了各种导出格式,同样的决定也适用于导出的文件。

把多位标注者合并为一个标签

聚合会把几位标注者给同一个条目的标签,变成模型据以训练的单一标签。最简单的方法是多数投票。多数投票把每位标注者的答案只计一次,并保留最常见的答案。Dawid and Skene (1979) 用期望最大化为每位标注者估计一个混淆矩阵,并按估计出的标注者可靠性给每一票加权。这样,粗心的标注者对结果的左右程度就比认真的标注者小。聚合众包标签比较了这两种方法,以及建立在 Dawid-Skene 之上的各种模型。

Potato 中的训练运行用 model_training.aggregation 合并标签。默认值 consensus 在项目有多于一位标注者时运行 Dawid-Skene,并在估计失败时退回多数投票。另一个值 majority 只运行多数投票。两种方法都把标注者在一个方案上的整个答案算作一票。因此,两个 multiselect 答案只有在勾选的标签集合完全相同时,才算同一票。

运行的 consensus 设置与共识导出是两个不同的功能。共识导出给每位标注者一票,报告每个条目上每个答案的计数和比例,并把每个条目标记为 retired 或 needs_review。想逐条目查看标注者在哪里出现分歧时,请在训练之前阅读该导出。MACE 则根据同样的投票为每位标注者估计一个能力得分。

从分歧中学习

在冒犯性或情感这类主观任务上,标注者可能因为各自对条目的理解不同而产生分歧,而单一的投票标签会丢掉这种分布。Uma et al. (2021) 综述了在分歧上训练而不是在一个聚合标签上训练的方法。这些方法中有一类在软标签上训练。软标签是标签上的概率分布,由选择每个标签的标注者所占比例构成。

hf-text-classification 接受参数 soft_targets: true。设置该参数后,被两位或更多标注者标注过的条目,会以对该分布的交叉熵,朝着标注者的分布训练。当三位标注者中有两位说 positive 时,目标是 0.67 的 positive 和 0.33 的 negative。对于 multiselect 方案,每个标签朝着勾选了该标签的标注者所占的比例训练。只有一位标注者的条目,仍照旧在那位标注者的标签上训练。这样,模型会在人们意见分歧的地方保持不确定,每个预测都包含模型的 probabilities。在存在投票分布的留出条目上,运行还会报告另外三项指标:

指标该指标衡量什么
soft_jsd模型的分布与标注者的分布之间的 Jensen-Shannon 散度,以比特为单位,取值 0 到 1
soft_cross_entropy标注者的分布在模型分布下的交叉熵
ece顶部标签相对于投票标签的期望校准误差,按 10 个置信度分箱计算

在软标签方案中,每位标注者用滑块给出一个分布。hf-text-classification 总是让 soft_label 方案朝着标注者的平均分布训练,预标签则把滑块设置为预测的分布。软目标要求一个条目上有两个或更多标签。因此,打算在分歧上训练的研究,从一开始就要收集有重叠的标签,需要多少标注者?讨论了重叠设置。分歧是信号,而非噪声阐述了把分布保留在数据中的理由。

train、验证和 test 划分

训练、验证和 test 划分承担三项不同的工作。训练划分拟合模型的权重。验证划分在候选模型之间做选择,例如选择在哪个 epoch 停止。test 划分估计在未见数据上的性能,并且不参与上述任何一项选择。在验证上选择检查点,是在拟合验证集的一种形式。因最佳验证得分而保留下来的检查点,其验证得分偏高,只有 test 得分来自选择过程从未参考过的条目。

Potato 的一次运行默认按 70/15/15 划分已标注的条目(splits)。每个条目所在的划分,来自对条目 ID 做的带种子哈希(split_seed)。因此,只要 split_seed 和 splits 保持不变,在一次运行中被留出的条目,在下一次运行中仍然被留出。更改任何一项设置都可能把留出条目移入训练,此后的每个得分便都部分地衡量了记忆。请在研究开始时一次性设置好这两项。

微调训练器(如 hf-text-classification)保留在验证上得分最高的那个 epoch 的检查点。这类运行的主得分来自 test。sklearn-text、setfit、rdkit-property、决策模型训练器和推理桥在验证上不做任何选择,只要验证划分中有条目,这些训练器之一的运行就从验证取主得分。Runs 表 Held-out score 列的提示文字会写明每个数字背后的划分和指标。有标签而没有预测的条目算作错误。训练器无法通过跳过难条目来抬高得分,<split>_coverage 会报告得到预测的条目所占的比例。

被更正和被固定的条目

有两类条目会进入之后每次运行的 train 划分,无论哈希把它们分到了哪个划分。第一类是审核者在模型审核中更正过的条目,第二类是有人在目录页面上某个切片旁用 Train on these 固定的条目。更正来自模型表现最差的那些条目,如果这些更正留在验证或 test 中,就等于用模型自己犯错的样本给模型评分。把留出条目移入训练的运行会记录一条带数量的警告,此后该次运行的留出得分,就无法与更早一次运行的得分直接比较。如果一次运行在某个更正过的条目上训练,而该条目在更早的运行中是被留出的,那么这次运行也会记录一条警告,因为此时在更早那次运行的验证或 test 条目上的得分,或者在由这些条目保存的数据集上的得分,就包含了新模型训练过的条目。更正和被固定的条目始终参与训练给出了相关规则。

记录每个模型训练过什么

比较两次运行,需要知道每次运行在哪些条目上训练和评分。每次 Potato 运行都会在 <output_annotation_dir>/training/runs/<run_id>/bundle/ 下写出一个数据包,包含 train.jsonl、val.jsonl、test.jsonl、labels.json 和 manifest.json。清单记录划分种子、聚合方法、每个划分中的条目数、被强制放入训练的条目数,以及划分文件的 SHA-256 摘要。数据包中没有任何内容回指 Potato,Potato 之外的脚本可以把数据包当作合并后标签与划分的自包含副本来读取。

项目数据库 project.sqlite 保存一份账本,每次运行一行。每一行记录训练器、参数、划分大小、划分种子、留出指标和数据包摘要。第二张表记录每个条目在每次运行中所处的划分。回写读取这张表,绝不会为账本中仍有的任何一次运行训练过的条目生成预标签(retain_runs 默认保留五次已完成的运行)。模型在它训练过的条目上有把握,其原因与新数据毫无关系。

要在完全相同的条目上比较之后的模型,请在运行页面上使用 Keep this split as a dataset,它会把该次运行的验证、test 或 train 条目,连同一致认可的人工标签,保存为数据集的一个版本。保存的版本此后不再改变。每个保存的版本只包含一个划分,并且从零开始,而不是在上一个版本上累加。否则,第 2 次运行的验证条目保存在第 1 次运行的之上,就会包含第 2 次运行训练过的条目。对保存版本的评估无法读到模型的答案,因为保存的输入略去了预标签字段。保存需要 datasets.enabled: true。当模型或数据集离开项目时,为数据集和模型编写文档介绍了数据表和模型卡。

第一个模型需要多少数据

每个 Potato 训练器在 train 划分中的条目少于 min_instances 个时都拒绝拟合。下面的默认值,就是一次运行得以开始时的条目数:

训练器min_instances 默认值说明
setfit8为项目的最初几天而设计,每个标签约八个条目起步
sklearn-text10只在训练条目达到 50 个以上时才校准置信度
hf-audio-tagger10
hf-text-classification, hf-token-classification, hf-image-classification20
llm-sft, llm-dpo20

下限只统计 train 划分。在默认的 70/15/15 划分下,当约 15 个条目有标签时,sklearn-text 运行就能开始。Potato 仓库中的模型训练示例给出了一个实测点。在示例的 160 条合成商品评论中,每条标注一次的 120 条上,一次 sklearn-text 运行在验证上得到 0.84 的 macro-F1,并发布了 80 个预标签。这个数字来自一个合成的二标签任务,对你自己的任务,应当看你自己运行的留出得分。

从 sklearn-text 开始。该训练器除普通安装外别无所需,也不需要 GPU。随着标签到来重新训练,并在 split_seed 不变的前提下,逐次运行地观察留出得分。预标签会锚定标注者。在留出得分足以支持展示模型的答案之前,请让 writeback.schemas 保持为空。模型辅助标注讨论标注者看到预标签之后会发生什么变化,如何区分认真审核与不加思考的接受则讨论如何检查标注者是否仍在认真阅读条目。

设定每项决定的配置

下面的配置在一个二标签情感方案上训练。model_training 下的每个键设定上述决定之一:

yaml
annotation_task_name: "Review sentiment"
task_dir: "."
data_files:
  - "data/reviews.json"
item_properties:
  id_key: "id"
  text_key: "text"
output_annotation_dir: "annotation_output"
 
annotation_schemes:
  - annotation_type: "radio"
    name: "sentiment"
    description: "Is this review positive or negative?"
    labels: ["positive", "negative"]
 
model_training:
  enabled: true
  aggregation: consensus
  split_seed: 0
  splits: {train: 0.7, val: 0.15, test: 0.15}
  trainer_params:
    hf-text-classification:
      soft_targets: true

aggregation: consensus 用 Dawid-Skene 合并有重叠的标签。split_seed 和 splits 按默认值写了出来,因为这两项设置在研究的整个生命周期内都不应改变。soft_targets: true 让项目上的每次 hf-text-classification 运行都在投票分布上训练。该训练器需要 pip install "potato-annotation[train-text]",而 sklearn-text 在同一配置上运行,无需额外安装。没有 writeback 块时,标注者看不到预标签。用这份配置重启服务器,打开 /admin/training,选择训练器和 sentiment 方案,然后点击 Train now。模型训练配置参考列出了每个键,循环中的模型教程则加入了回写和自动重新训练。

延伸阅读

参考文献

  • Dawid, A. P., and Skene, A. M. (1979). Maximum Likelihood Estimation of Observer Error-Rates Using the EM Algorithm. Applied Statistics, 28(1), 20. doi.org/10.2307/2346806
  • Uma, A. N., Fornaciari, T., Hovy, D., Paun, S., Plank, B., and Poesio, M. (2021). Learning from Disagreement: A Survey. Journal of Artificial Intelligence Research, 72, 1385–1470. doi.org/10.1613/jair.1.12752