跳转到内容

一致性漂移与再校准轮次

Potato 在 /admin/iaa 上按已完成条目的窗口计算一致性,在一致性低于项目基线时给出提示,并让编码者重新回到培训阶段。

Potato 在先后相继的已完成条目窗口中计算编码者间一致性,因此近期编码质量的下滑会在一致性报告上表现为一个偏低的窗口,而不会被平均进一个全项目的数字里。全项目系数把第一周的编码和最后一周的编码混在一起,一个近期工作已经走样的团队仍可能报告一个可以接受的 α。/admin/iaa 上的 Agreement over time 部分逐个窗口显示每个标注方案的系数,并在最近一个窗口比项目基线低出设定的比例时给出再校准提示。随后,再校准轮次会让编码者重新做一遍培训条目,并用已知的正确答案为编码者的回答评分。时间线和再校准轮次从 Potato v2.9.0 起就已提供,时间线默认开启。

这里的漂移(drift)指在一项研究的进行过程中,编码者应用码本的方式逐渐发生的变化。随着新的边缘案例出现,每名编码者各自形成对某条定义的私人理解,漂移就逐渐累积起来。编码开始前的一次性编码者间信度检验无法发现漂移。Hennessy、Bleakley 和 Ellithorpe(2022)引用了 Lacy 等人(2015)的一段话,Lacy 等人写道:"the intercoder reliability check should occur with study content as it is coded."(编码者间信度检验应当在研究内容被编码的同时进行。)同样的问题也适用于标注指南随标签集扩大而不断变化的机器学习标注团队。本页讨论研究进行过程中的一致性,而时间片段上的一致性讨论标注者在音频和视频中标记起止时间的情况。

窗口边界与条目时间戳

一致性属于跨编码者的某个条目,而不属于某一名编码者的答案。因此,Potato 把每个条目放进包含该条目最后一名编码者完成时刻的那个窗口。那个时刻是该条目第一次拥有一整套可供比较的答案的时刻。只有达到标注者上限的条目才会进入时间线,而且上限必须是两名或更多(例如 num_annotators_per_item: 2)。全项目一致性表使用的是同一批条目。因此,每个窗口包含的是基线背后那批条目中的一部分。

编码者完成某个条目的时刻,是该方案答案的最后一次记录在案的修改。条目没有任何修改记录时,Potato 依次退而使用编码者离开该条目的时间,再退而使用编码者打开该条目的时间。打开时间排在最后,因为一名先浏览完队列、稍后再作答的编码者,打开每个条目的时间远早于作答时间。完全没有时间戳的条目不进入时间线,该部分会报告这类条目的数量。从其他工具导入的标注是最常见的情况。

条目按这个时刻排序,再用以下两种方式之一切分成窗口:

  • 等数量(window_by: count,默认)。每个窗口包含相同数量的条目。窗口数不超过条目数。因此,一个只有五个条目的试点不会产生空窗口。
  • 等时长(window_by: time)。每个窗口覆盖相同长度的时钟时间。编码集中在几段时间进行时,例如相隔一个月的两个周末,两段编码之间的窗口就是空的。空窗口根本没有一致性数值。在一致性页面的表格中,空窗口看起来会像一致性骤降,而实际上那段时间没有人编码,等数量因此被设为默认方式。

每个窗口报告哪个系数

Potato 用每个窗口自己的条目重新运行全项目的一致性计算。因此,对于同一个方案,每个窗口和基线使用同一个系数。Potato 为每个方案选择一个主系数,并优先使用经机遇校正的指标。百分比一致率仅因类别不平衡就会上升,即使编码者的一致程度低于随机水平,它也可能呈上升趋势。系数取决于方案类型:

方案类型时间线上的系数
radio、select(每段摘录一个代码)Krippendorff's α,名义尺度
likertKrippendorff's α,顺序尺度
slider、numberKrippendorff's α,等距尺度
multiselect、hierarchical_multiselect(每段摘录多个代码)使用 MASI 距离的 Krippendorff's α
span(已编码的段落)位置 α;位置 α 无定义时改用词元级 κ
multirate 及其他网格类方案对所有行合并计算的 α
没有 α 的方案依次取 Fleiss' κ、Cohen's κ、加权 κ、相关系数或 ICC、片段 F1 和百分比一致率中第一个可用的指标

Krippendorff's α 在完全一致时为 1,在一致程度等于随机水平时为 0。textbox 等自由文本方案没有一致性指标,在时间线上没有对应的行。当某个窗口的数据无法算出基线所用的系数时,该单元格不显示数值,而不是改用另一种指标。两种不同指标之间的下降没有意义。

时间线单元格与单元格注记

每个方案占一行。行首是作为基线的全项目系数,后面每个窗口一个单元格(W1、W2 等),显示该窗口的系数和条目数。没有数值的单元格会显示一条简短的注记,Potato 在时间线表格下方解释每条注记。条目太少的窗口仍显示数值,并附加一个标记。各条注记和该标记的含义如下:

注记含义
total agreement在该窗口的每个条目上,所有编码者都选择了同一个代码。经机遇校正的系数没有可校正的分歧,因而无定义。这条注记标示的是一致性最好的情况。
undefined对于该窗口的数据,系数无定义,最常见的原因是所有判断都落在同一个代码上。
no overlap在该窗口的条目上重叠的编码者少于两名。
no items没有条目落入该窗口(仅限等时长窗口)。
too few to judge该窗口少于五个条目。数值照常显示,但再校准提示会忽略该窗口。

如果项目使用 QDA 模式码本,表格下方会有一行列出码本修订版本(r1 · r2 · r3)。码本每发生一次变更,Potato 就把修订号加一。新建、重命名、更改颜色、移动或删除一个代码,都各算一次变更。自 v2.9.0 起记录的修订版本带有精确时间。更早的修订版本按带有该修订号的最早一条标注确定日期。这个日期是一个下界,该修订版本会被标为 approximate。HTML 页面按编号列出修订版本,下文的 JSON 报告则给出每个修订版本的时间。脚本可以把每次码本编辑与窗口边界对照起来,看某次指南修改是否改变了一致性。

配置

时间线不需要任何配置。任何每个条目有两名或更多编码者并启用了一致性报告的项目,都会在 /admin/iaa 上看到这一部分。管理后台把该页面作为完整的一致性报告链接出来。要修改默认值,在现有配置中加入一个 calibration 代码块:

yaml
calibration:
  enabled: true
  windows: 6
  window_by: count
  drop_threshold: 0.15

calibration 只接受以下四个键:

键类型默认值作用
enabledbooleantrue每次加载 /admin/iaa 时计算时间线。只有 false 会关闭这一部分。
windowsinteger6窗口数。六个窗口既能显示趋势,又能让每个窗口有足够的条目来计算经机遇校正的系数。在等数量模式下,窗口数不超过条目数。
window_bystringcountcount 表示等大小窗口,time 表示等时长窗口。其他任何值都按 count 处理。
drop_thresholdnumber0.15触发再校准提示的、相对基线的下降比例。0.15 表示比全项目系数低 15%。

每次加载 /admin/iaa,时间线都要为每个窗口多做一次一致性计算。在大型项目上,设置 enabled: false 可以省去这部分开销。此时时间线会同时从 /admin/iaa 和 /admin/iaa/drift JSON 报告中消失。

再校准轮次会重新运行培训阶段。因此,计划进行再校准轮次的项目需要启用 training,并在 phases 中有一个 training 条目。下面的配置是一项完整的双编码者研究,对开放式调查回答进行编码,每个回答一个代码:

yaml
annotation_task_name: Open-ended responses, two coders
task_dir: .
output_annotation_dir: annotation_output/
data_files: [data/responses.json]
item_properties: {id_key: id, text_key: text}
 
annotation_schemes:
  - annotation_type: radio
    name: barrier
    description: Which barrier to care does the respondent describe?
    labels: [Cost, Distance, Wait time, Trust, None]
 
num_annotators_per_item: 2
 
training:
  enabled: true
  data_file: data/training.json
  allow_retry: true
  failure_action: repeat_training
 
phases:
  order: [training, annotation]
  training: {type: training}
  annotation: {type: annotation}
 
agreement_metrics: {enabled: true}
 
calibration:
  windows: 6
  window_by: count
  drop_threshold: 0.15

num_annotators_per_item: 2 让每段摘录都交给两名编码者。每段摘录有两名编码者,每个窗口才有一致性数值。failure_action: repeat_training 让未通过再校准轮次的编码者重新做培训条目,而不是结束该编码者的参与。培训文件包含带有商定代码和解释的摘录,格式见培训阶段页面。

再校准提示

Potato 把每个方案的最近一个窗口与基线比较,当相对下降达到 drop_threshold 时给出提示。相对下降为 (基线 − 最近窗口) / 基线。基线取全项目系数而不是第一个窗口的系数,因为刚完成培训的团队在第一个窗口中一致性最不稳定。否则,一个异常好的第一个窗口会为整个研究定下标准。若基线 α 为 0.720,最近一个窗口为 0.580,下降为 19%,该部分会显示:

text
Re-calibration suggested — barrier 19% below the project baseline (alpha_nominal 0.580 vs 0.720).

这条提示是给团队负责人的建议。提示不会通知编码者,也不会启动再校准轮次。以下三种情况下,Potato 不会给出提示:

  • 更早的下降。 Potato 只判断最近一个包含五个或更多条目的窗口。如果针对团队已经恢复过来的下降发出提示,团队会学会忽略提示。
  • 最近一个窗口无定义。 当最近一个窗口显示 total agreement 或 undefined 时,Potato 不会退回去使用更早的窗口。
  • 基线等于或低于零。 一致性已处于随机水平是全项目表格会报告的问题,而从零开始的百分比下降没有意义。

通过管理 API 启动校准轮次

一轮再校准会重置每名指定编码者的培训进度,并让该编码者回到培训阶段。编码者再做一遍同样的培训条目。Potato 用同样的正确答案为回答评分,并像第一遍那样为每个错误答案显示解释。通过之后,编码者进入 phases.order 中培训之后的阶段,并从上次停下的地方继续。再校准轮次既不改变分配,也不改变已保存的标注。Potato 不会向编码者显示任何表明再校准轮次已开始的消息。启动之前请先告知团队。

再校准轮次通过管理 API、使用管理员 API 密钥启动。管理页面上没有启动再校准轮次的按钮。管理后台页面说明了密钥从哪里获得。查看哪些编码者符合条件、上一轮结果如何:

bash
curl -H "X-API-Key: $KEY" http://localhost:8000/admin/calibration

响应列出可被召回的编码者、以往的轮次,以及每名被召回编码者在最近一轮中的进度。进度是 passed、failed、in_progress 或 unknown 之一,并附有已答、总数和答对的计数。处于标注阶段、研究后阶段或完成阶段的编码者符合条件。为所有符合条件的编码者启动一轮:

bash
curl -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
     -d '{"reason": "barrier alpha fell 19%"}' \
     http://localhost:8000/admin/calibration/start

该调用返回轮次的 ID、被召回的编码者,以及被跳过的编码者及其原因。传入 "usernames": ["coder1", "coder2"] 可以只召回指定的编码者。省略 usernames 则召回所有符合条件的编码者。指南漂移影响整个团队。因此,召回所有人通常是正确的选择。

仍停留在知情同意页或说明页的编码者会列在响应的 skipped 下。未启用 training 时,该调用返回 HTTP 400,不召回任何人。没有培训条目,被召回的编码者会直接通过一个空的培训阶段。

在第一轮再校准之前规划好培训设置,原因有两个。第一,未通过再校准轮次的编码者由 training.failure_action 处理。在默认的 move_to_done 下,未通过再校准轮次会结束该编码者在研究中的参与。未通过第一遍培训也是同样的结果。设置 failure_action: repeat_training 可以改为让编码者重新做培训条目。第二,再校准轮次按 ID 选取培训条目,向每名编码者展示该编码者第一次看到的条目。编码者完成第一遍培训之后才加入文件的培训条目,不会在再校准轮次中出现在该编码者面前。请在编码开始前把疑难案例放进培训文件。

团队在哪里讨论分歧

再校准轮次重新运行培训条目,因为这些条目有可供评分的已知答案。研究本身存在分歧的条目没有标准答案。因此,Potato 不会把这些条目放进再校准轮次。再校准中的讨论环节在 Potato 的其他地方进行。多人协作房间中的碰头会房间(huddle)会带领团队逐一查看团队当前存在分歧的条目,并显示每名编码者最初给出的代码。在裁定队列中,裁定者逐条解决有争议的条目。质性研究团队通常以修订码本中的代码定义来结束这类讨论,下一个修订版本随后会出现在时间线上、位于之后各窗口的旁边。

漂移报告与轮次日志

漂移报告和轮次日志独立于标注导出。没有任何 python -m potato.export 格式包含漂移报告或轮次日志。以 JSON 格式获取时间线:

bash
curl -H "X-API-Key: $KEY" http://localhost:8000/admin/iaa/drift

JSON 报告包含以下字段:

  • windows 列出每个窗口的序号、起止时间、条目数、sparse 标志、条目 ID,以及限定在该窗口内的完整一致性报告。
  • schemas 按方案给出主指标、基线,以及每个窗口一个数据点,包含该窗口的数值、注记和条目数。
  • triggers 列出每个触发了提示的方案,附带基线、最近值和相对下降。
  • markers 列出码本修订版本,附带每个修订版本的时间,以及标示近似日期的 inferred 标志。
  • n_items 和 n_untimed_items 统计时间线上和时间线外的条目数,reason 说明报告为空的原因。

时间均为以秒为单位的 Unix 时间戳。没有可比较的内容时报告为空,例如没有任何条目达到标注者上限,或所有带时间戳的条目都落在同一个窗口中。此时 reason 字段会说明原因。

Potato 把每一轮记录在 <task_dir>/project.sqlite 的 calibration_rounds 表中,包括该轮的开始时间、启动该轮的管理员(通过 API 密钥调用时为 admin)、原因文本,以及被召回的编码者。轮次日志和分窗口的系数为方法部分提供每次再校准的日期,以及每轮前后的一致性。质性研究的审计轨迹需要这两份记录。编码者的培训答案保存在 user_state.json 中,每一轮再校准都会重置这些答案。要保留第一遍培训的得分,请在每轮开始前保存一份标注输出目录的副本。

延伸阅读

实现细节请参阅源文档。

参考文献

  • Hennessy, M., Bleakley, A., & Ellithorpe, M. E. (2022). Evaluating and tracking qualitative content coder performance using item response theory. Quality & Quantity, 57(2), 1231-1245. https://doi.org/10.1007/s11135-022-01397-7
  • Lacy, S., Watson, B. R., Riffe, D., & Lovejoy, J. (2015). Issues and best practices in content analysis. Journalism & Mass Communication Quarterly, 92(4), 791-811. https://doi.org/10.1177/1077699015607338(转引自 Hennessy et al., 2022)