众包投票、点标记与边界框的共识聚合
把多名标注者对每个条目的答案合并为一个共识,给出投票占比、DBSCAN 点聚类和中位数边界框,做法与 Zooniverse 的 reducer 一致。
v2.13.0 新增
consensus 导出把多名标注者对每个条目的答案合并为每个条目一个共识答案,做法与 Zooniverse 等公民科学项目合并志愿者分类结果的方式相同。在志愿者项目中,每个对象(subject)会分发给多名志愿者,例如数一张群落照片中的企鹅,或标出一帧红外相机图像中的动物。对于选择题,导出报告每个答案有多少名标注者选择,并把每个条目标记为 retired 或 needs_review。对于点标记,导出把标注者的点聚类为每个目标一个点;对于边界框,导出为每个目标报告一个中位数框。公民科学以外的众包团队也用同一个导出把每个条目上的多个标签合并为一个标签。沿用 Zooniverse 的说法,Potato 把在某个条目上回答了某个问题的标注者称为该条目的分类者(classifier)。
- 选择题:
radio、select、likert和multiselect方案会得到每个答案的人数和占比、得票最多的答案、平票情况以及状态。 - 数值题:
slider和number方案会得到人数、均值、中位数、标准差、最小值和最大值。 - 点:
image_annotation方案中的landmark标记用 DBSCAN 聚类,半径以像素为单位,或以图像尺寸的比例为单位。 - 框:
image_annotation方案中的bbox标记按重叠程度分组,每组得到一个中位数框。 - 安装: 这个导出只需要 Potato 本身。聚类用到 numpy,而 numpy 是 Potato 的核心依赖,会随 Potato 一起安装。
共识与一致性
Potato 衡量标注者之间的一致程度,而共识导出为每个条目给出一个答案。reliability 导出和 /admin/iaa 页面会针对整个研究中的每个问题报告 Krippendorff's alpha,alpha 偏低的团队首先应检查标注指南或标注者培训。共识导出逐条目工作。每个条目都有一个状态,记录该条目上的标注者是否足够一致,可以保留这个答案。依赖共识之前先检查一致性,因为共识导出不论标注者的准确率如何,都给每名标注者一票。关于空间标签上经过机遇校正的一致性,参见如何测量边界框上的标注者间一致性。
MACE 对每名标注者的投票加权,而不是把每一票同等计数。MACE 在 radio、likert、select 和 multiselect 方案上为每名标注者估计一个能力分数,并根据加权后的投票为每个条目推断一个标签。聚合众包标签讨论了能力模型何时优于不加权投票,以及何时应保留答案的分布。MACE 不读取点标记或框标记。对于图像上的标记,请使用共识导出。
配置
下面的配置搭建一个企鹅计数工作流,包含一个三选一问题、一个多选问题,以及一个图像方案;在图像方案中,标注者用点标出每只成年企鹅,并在每个巢周围画一个框:
annotation_task_name: "Penguin colony counts"
task_dir: .
data_files: [data/colonies.jsonl]
item_properties: {id_key: id, text_key: image}
output_annotation_dir: annotation_output
num_annotators_per_item: 5
user_config:
allow_all_users: true
annotation_schemes:
- annotation_type: radio
name: T0
description: "Are there penguins in the image?"
labels: ["Yes", "No", "Unsure"]
- annotation_type: multiselect
name: T1
description: "What else is in the image?"
labels: [Ice, Water, Rock]
- annotation_type: image_annotation
name: T2
description: "Mark each adult penguin and draw a box round each nest"
source_field: image
tools: [landmark, bbox]
labels:
- {name: Adult, color: "#00ff00"}
- {name: Nest, color: "#ff0000"}
consensus_aggregation:
agreement_threshold: 0.8
min_classifiers: 2
points:
eps: 10
eps_unit: pixels
min_samples: 2
boxes:
iou_threshold: 0.5方案名 T0、T1 和 T2 是 Zooniverse 的任务 ID。项目从 Zooniverse 导入时,potato import 会把任务 ID 保留为方案名;在 Potato 中搭建的研究可以使用任意名称。Potato 把每张图像分发给五名标注者(num_annotators_per_item: 5)。在 agreement_threshold: 0.8 下,五名标注者中须有四名给出相同答案,条目才会退役。在 min_classifiers: 2 下,单个标注者的答案永远不算共识。彼此相距 10 像素以内的两个点可以形成一个簇;两个框只有在交并比(IoU)至少为 0.5 时才算同一个巢。
要以像素半径聚类,导出需要知道每张图像的尺寸。每个条目给出图像,以及图像以像素计的宽和高:
{"id": "9001", "image": "/media/colony_9001.png", "image_width": 1000, "image_height": 500}这个块中的每个键都是可选的:
| 键 | 默认值 | --option 名称 | 含义 |
|---|---|---|---|
agreement_threshold | 0.8 | agreement_threshold 或 threshold | 条目退役前,得票最多的答案需要达到的分类者占比。大于 0 且不超过 1 |
min_classifiers | 2 | min_classifiers | 得到 needs_review 以外状态所需的最少分类者人数。整数,至少为 1 |
schemes | 所有受支持的方案 | schemes(逗号分隔) | 要聚合的方案名。每个名称都必须是配置中声明的方案 |
points.eps | 0.02 | eps | 聚类半径。大于 0;当 eps_unit 为 fraction 时不超过 1 |
points.eps_unit | fraction | eps_unit | fraction 把 x 量为图像宽度的比例、把 y 量为高度的比例,pixels 以图像像素度量 |
points.min_samples | 2 | min_samples | 一个点成为核心点所需的、位于其 eps 范围内的最少点数。计数包含该点自身。整数,至少为 1 |
boxes.iou_threshold | 0.5 | iou_threshold | 一组中任意两个框之间的最小 IoU。大于 0 且不超过 1 |
当某个值超出范围、某个键不在该块的键列表中,或 schemes 中的某一项指向配置未声明的方案时,服务器会在启动时停止,并打印一条指明该键的消息。python -m potato.validate_cli config.yaml 会在研究开始之前报告同样的错误。
导出命令
导出读取配置以及保存在 output_annotation_dir 下的标注:
python -m potato.export -c config.yaml -f consensus -o consensus_out该命令把 consensus.json 以及与配置中方案类型对应的 CSV 文件写入 consensus_out/。传入 --option 可以在单次运行中覆盖该块的某项设置,而不修改配置:
python -m potato.export -c config.yaml -f consensus -o consensus_out \
--option agreement_threshold=0.9 --option eps=8 --option eps_unit=pixels遇到未知的选项名时,导出会停止并打印已知名称的列表。如果配置中没有受支持类型的方案,或者没有保存任何标注,导出也会停止并打印原因。对于无法通过 shell 访问服务器的团队,/admin/api/export 处的管理员导出 API 以格式名 consensus 提供同样的导出。
选择题
对于每个条目和问题,导出统计分类者人数,以及给出每个答案的标注者人数。每个答案的占比等于该答案的人数除以分类者人数。对于单选题,各占比之和为 1。对于 multiselect,每个占比是勾选该选项的分类者所占的比例,各占比之和可以大于 1。consensus_questions.csv 每个条目和问题占一行。以下各行来自本页末尾介绍的示例项目:
instance_id,schema,type,n_classifiers,top_answer,top_count,top_fraction,tied,status,reason
9001,T0,radio,5,Yes,4,0.8,,retired,
9001,T1,multiselect,5,Ice,4,0.8,,needs_review,no agreement on Water (ticked by more than 20% and fewer than 80% of classifiers)
9002,T0,radio,4,Yes,2,0.5,Yes;No,needs_review,"tie between Yes, No"
9002,T1,multiselect,4,Rock,4,1.0,,retired,
9003,T0,radio,1,Yes,1,1.0,,needs_review,"1 classifier, fewer than min_classifiers (2)"
9003,T1,multiselect,1,Ice,1,1.0,,needs_review,"1 classifier, fewer than min_classifiers (2)"状态遵循三条规则:
- 分类者过少: 分类者少于
min_classifiers的条目为needs_review。 - 单选题: 对于
radio、select或likert条目,当得票最多的答案占比至少达到agreement_threshold,且没有其他答案与之平票时,条目为retired。 - 多选题: 当每个选项都已确定时,条目为
retired。一个选项在至少agreement_threshold比例的分类者勾选了它,或至少同样比例的分类者没有勾选它时,即为已确定。
在图像 9001 上,五名标注者中有两名勾选了 Water。因此 Water 选项仍未确定:勾选 Water 的 40% 和未勾选 Water 的 60% 都没有达到 0.8 的阈值。出现平票时,top_answer 按方案声明标签的顺序给出第一个平票答案,tied 用分号分隔列出所有平票答案。reason 列说明条目为何需要复核。
共识状态只是一份报告,Potato 不会根据状态采取任何操作。Zooniverse 在对象收到设定数量的分类后让其退役,而 Potato 无论共识状态如何,都会按研究的分配规则继续分配条目。可以用状态来挑选需要发给更多标注者或交付裁定的条目。
consensus_votes.csv 每个条目、问题和答案占一行,列为 instance_id, schema, answer, count, fraction, n_classifiers。方案声明的每个选项都有一行,没有标注者选择的选项人数为 0:
instance_id,schema,answer,count,fraction,n_classifiers
9001,T0,Yes,4,0.8,5
9001,T0,No,1,0.2,5
9001,T0,Unsure,0,0.0,5
9001,T1,Ice,4,0.8,5
9001,T1,Water,2,0.4,5
9001,T1,Rock,1,0.2,5如果保存的多选记录列出了选项但一个都没有勾选,就算作一名没有投票的分类者。这样的记录使 n_classifiers 加一,但不增加任何答案的人数。没有该问题任何条目的记录不算作分类者。
滑块题与数字题
consensus_numeric.csv 每个条目和问题占一行,列为 instance_id, schema, type, n, mean, median, sd, min, max。n 是给出数值答案的标注者人数。sd 是分母为 n − 1 的样本标准差。只有一名标注者作答时,sd 为空。数值题没有状态。
点聚类
DBSCAN(density-based spatial clustering of applications with noise,基于密度的含噪声空间聚类)把彼此靠近的点归为一组,并把孤立的点作为噪声排除在外。导出把所有标注者在同一条目上同一标签的点汇集在一起,再用 DBSCAN 对汇集后的点聚类:
- 若一个点与其
eps范围内的其他点合计至少有min_samples个,该点就是核心点。 - 一个簇由一组相互连通的核心点,加上位于其中某个核心点
eps范围内的其他点构成。紧邻两个簇的点加入先找到的那个簇,顺序取决于标注的加载顺序。 - 其余的点都是噪声。
这条规则与 scikit-learn 的 DBSCAN 所实现、panoptes_aggregation 的 reducer point_reducer_dbscan 所调用的规则相同。在 eps_unit: pixels 且 eps 和 min_samples 相同的情况下,导出给出的簇和中心与 point_reducer_dbscan 相同。panoptes_aggregation 默认 eps 为 5 像素、min_samples 为 3,而 Potato 的默认值适合每个条目只有少数几名标注者的情形。如果每张图像有很多志愿者,请调高 min_samples。
consensus_points.csv 每个条目、方案、标签和簇占一行:
instance_id,schema,label,cluster,x,y,x_px,y_px,size,n_annotators,spread,spread_px,var_x,var_y,cov_xy,annotators,label_points,label_annotators,label_noise,n_classifiers,note
9001,T2,Adult,0,0.10025,0.201,100.25,100.5,4,4,0.003897,2.331845,2.916667,4.333333,-1.166667,vol1;vol2;vol3;vol4,8,4,1,4,
9001,T2,Adult,1,0.800667,0.8,800.666667,400.0,3,3,0.004643,3.681787,16.333333,4.0,8.0,vol1;vol2;vol4,8,4,1,4,
9002,T2,Adult,,,,,,,,,,,,,,1,1,1,1,no cluster (every point is noise)在图像 9001 上,四名标注者放置了八个 Adult 点。其中七个点构成两个簇,每个簇对应一只成年企鹅,第八个点是噪声。各列含义如下:
| 列 | 含义 |
|---|---|
cluster | 该条目上该标签内的簇编号,从 0 开始 |
x, y | 簇中心,即成员点的均值,以图像宽度和高度的比例表示 |
x_px, y_px | 以像素表示的同一中心,图像尺寸未知时为空 |
size | 簇中的点数 |
n_annotators, annotators | 在该簇中有点的不同标注者人数,以及这些标注者的名字 |
spread, spread_px | 成员点到中心距离的均方根,分别以比例和像素表示 |
var_x, var_y, cov_xy | x 和 y 的样本方差以及 x 与 y 的协方差,图像尺寸已知时以像素为单位。这些值与 Zooniverse reducer 的 clusters_var_x、clusters_var_y 和 clusters_var_x_y 列一致 |
label_points, label_annotators, label_noise | 该条目上整个标签的点数、标注者人数和噪声点数,在该标签的每一行上重复 |
n_classifiers | 在该条目上保存了图像方案的标注者人数 |
note | 标签没有簇的原因 |
如果一个标签的点全部是噪声,它仍会得到一行,cluster 为空并带有 note,如图像 9002 所示。DBSCAN 并不阻止同一名标注者的两个点落入同一个簇。比较 size 与 n_annotators 即可找出这类簇。
图像尺寸与 Zooniverse 导入
Potato 把每个标记都存储为图像宽度和高度的比例。因此在 eps_unit: fraction 下,导出无需图像尺寸即可对每个条目上的点聚类。在一张 1000 × 500 像素的图像上,0.02 的 eps 在水平方向相当于 20 像素,在竖直方向相当于 10 像素。
像素输出和 eps_unit: pixels 都需要图像尺寸。导出从条目的 image_width 和 image_height 字段(或 width 和 height)获取尺寸。没有这些字段时,如果图像文件位于任务的媒体目录下,导出会从图像文件本身读取尺寸。远程图像不会被下载。在 eps_unit: pixels 下,尺寸未知条目上的标签会得到一行,附注为 "image size unknown; pixel eps cannot be applied",导出还会打印一条警告,列出至多五个这样的条目。
Zooniverse 的对象是托管在 Zooniverse 服务器上的图像,导出无法打开这些服务器上的图像。从 Potato 2.13.0 起,对于含有绘制任务的项目,potato import 会在每个条目上写入 image_width 和 image_height。尺寸来自 Zooniverse 随分类结果记录的图像尺寸,或来自对象元数据中的宽和高。因此导入的绘制结果可以按像素聚类,所用的 eps 与团队在 panoptes_aggregation 中使用的相同。导入时请加上 --keep-annotators。加上这个参数后,导入器会把每名志愿者的分类结果写成该志愿者自己保存的标注,共识导出随后对每名志愿者只计一次:
potato import -i zooniverse-exports/ -o my-project --keep-annotators
python -m potato.export -c my-project/config.yaml -f consensus -o consensus_out未登录的志愿者显示为 anon_<id>,因此同一个人可能被算作多名分类者。没有记录尺寸的对象上的标记会在导入时被丢弃,导入过程会统计被丢弃的标记数并打印警告。Zooniverse 导入一节列出了导入器能读取的任务类型。
共识框
交并比(IoU)是两个框的公共面积除以两个框合起来覆盖的面积。导出把同一条目上同一标签的框分成若干组。每组中每名标注者至多有一个框,组内每一对框的重叠都至少达到 iou_threshold。导出分三步建组:
- 每个剩余的框各自发起一个候选组,其他标注者的剩余框按与该首个框的 IoU 从高到低依次尝试。一个框与组内已有的每个框的 IoU 都达到阈值时,即加入该组。
- 保留最大的候选组。规模相同时,选与首个框 IoU 之和更大的组,再相同则选首个框更靠前的组。
- 被保留组中的框从池中移出,对剩余的框重复上述步骤。不与任何其他框重叠的框自成一组。
两个框若都与第三个框重叠、彼此却不重叠,最终会落入不同的组。共识框取成员框左、上、右、下四条边各自的中位数。图像被拉伸时 IoU 不变。因此分组无需图像尺寸,图像尺寸已知时会填入像素列。
consensus_boxes.csv 每个条目、方案、标签和组占一行:
instance_id,schema,label,group,x,y,width,height,x_px,y_px,width_px,height_px,support,n_classifiers,support_fraction,mean_iou,min_iou,annotators
9001,T2,Nest,0,0.2,0.2,0.1,0.11,200.0,100.0,100.0,55.0,3,4,0.75,0.85617,0.826087,vol1;vol2;vol3
9001,T2,Nest,1,0.6,0.6,0.05,0.1,600.0,300.0,50.0,50.0,1,4,0.25,1.0,1.0,vol4
9003,T2,Nest,0,0.01,0.04,0.03,0.08,10.0,20.0,30.0,40.0,1,1,1.0,1.0,1.0,vol1x、y、width 和 height 以图像比例给出共识框,_px 列以像素给出同一个框。support 是该组中的标注者人数,n_classifiers 是在该条目上保存了图像方案的标注者人数,support_fraction 等于 support 除以 n_classifiers。mean_iou 和 min_iou 将每个成员框与共识框比较。在图像 9001 上,四名标注者中有三名画出了第一个巢,与中位数框的平均 IoU 为 0.86;另一名标注者的框与其他任何框的重叠都未达到阈值,自成一组。
这种分组方法是 Potato 自己的,并非 Zooniverse 框 reducer 的移植。panoptes_aggregation 的 rectangle_reducer 以像素为单位,对每个框的 x、y、宽和高运行 DBSCAN。shape_reducer_dbscan 在 metric_type: IoU 下以 1 − IoU 为距离运行 DBSCAN,一串相互重叠的框可能把两个彼此并不重叠的框连到一起。因此,对于同一批分类结果,Potato 和 Zooniverse 得出的框共识可能不同。
文本及其他答案类型
共识导出读取七种方案类型:radio、select、likert、multiselect、slider、number 和 image_annotation。自由文本、片段以及其他所有方案类型都不参与共识,也不会给出警告。要进一步缩小范围,请设置 schemes。文本答案没有可计的票,表格导出会保留每名标注者的文本,便于并排阅读或用于裁定。
在 image_annotation 方案内,导出只聚合点和框。多边形、折线、椭圆、自由形状、掩码和关键点组按类型计数,导出会在一条警告中列出这些计数。consensus.json 在 not_aggregated 下记录同样的计数。
输出文件
| 文件 | 每行对应 | 写出条件 |
|---|---|---|
consensus_questions.csv | 条目与问题 | 配置中有 radio、select、likert 或 multiselect 方案 |
consensus_votes.csv | 条目、问题与答案 | 同上 |
consensus_numeric.csv | 条目与问题 | 配置中有 slider 或 number 方案 |
consensus_points.csv | 条目、方案、标签与点簇 | 配置中有 image_annotation 方案 |
consensus_boxes.csv | 条目、方案、标签与框组 | 同上 |
consensus.json | 以上结果的嵌套形式,附带所用设置、not_aggregated 计数和警告 | 总是写出 |
CSV 文件中的数值保留六位小数。annotators 和 tied 等列表用分号连接。consensus.json 保留完整数值以及每个问题的分类者列表。
示例
python potato/flask_server.py start examples/image/zooniverse-aggregation/config.yaml -p 8000这个示例是一个虚构的企鹅计数工作流,从 Zooniverse 导出文件导入,包含五名志愿者在四张群落图像上保存的分类结果。本页的 CSV 行来自对该示例运行共识导出的结果,在 Potato 源码目录中用 python -m potato.export -c examples/image/zooniverse-aggregation/config.yaml -f consensus -o consensus_out 运行。
延伸阅读
- 聚合众包标签 - Dawid-Skene、MACE,以及何时保留答案的分布
- MACE 能力估计 - 面向类别型方案的能力加权标签
- 如何测量边界框上的标注者间一致性 - 空间标签上经过机遇校正的一致性
- 导出格式 - Potato 的导出格式和导出命令
- 生态学 - 用 Potato 做红外相机与生物声学标注
实现细节参见源文档。