Skip to content

最优-最差量表(Best-Worst Scaling)

在 Potato 中运行最优-最差量表:标注者在每个元组里选出最好和最差的项目,再由计数法、Bradley-Terry 或 Plackett-Luce 换算成分数。

v2.3.0 新增

Best-Worst Scaling(BWS),也叫 MaxDiff,把一组项目(通常是四个)摆在标注者面前,请他按某个标准选出最好的和最差的。每个元组点两下,得到的信息比打一次分更多;而且因为判断是比较式的,它绕开了"一个标注者的 7 分相当于另一个标注者的 5 分"这个麻烦。

以下情况值得为 BWS 做这套配置:

  • 直接打数值分会受到标注者之间量表使用习惯差异的影响
  • 你需要在成百上千个项目上得到一份可靠的排名
  • 质量维度本身就是相对的,比如哪个译文读起来最流畅

Best-worst scaling annotation interfaceBest-worst scaling interface for comparative annotation in Potato

配置

一个 BWS 任务分成两部分。方案规定问标注者什么,顶层的 bws_config 块规定元组怎么生成、怎么评分。

yaml
bws_config:
  tuple_size: 4              # items shown at once
  min_item_appearances: 8    # how often each item must appear across tuples
  seed: 42
  scoring:
    method: counting         # counting | bradley_terry | plackett_luce
 
annotation_schemes:
  - annotation_type: bws
    name: fluency
    description: "Select the BEST and WORST translation by fluency"
    tuple_size: 4
    best_description: "Most Fluent"
    worst_description: "Least Fluent"
    sequential_key_binding: true
    label_requirement:
      required: true

方案只接受 tuple_size、best_description、worst_description、sequential_key_binding 和 label_requirement,此外没有别的。凡是和生成元组或给元组评分有关的,都写在 bws_config 里。

缺了 bws_config 块的 bws 方案,会在一个空的项目列表上渲染问题——BWS 任务打开是一片空白,通常就是这个原因。

bws_config 选项

选项说明
tuple_size一个元组里同时显示多少个项目
num_tuples总共生成多少个元组
min_item_appearances每个项目至少要出现在多少个元组里
seed固定采样,使一次运行可以复现
scoring.methodcounting、bradley_terry 或 plackett_luce

调高 min_item_appearances 会收紧每个项目的分数,代价是更多的比较次数。分数出来噪声大的时候,拧的就是这个旋钮。

数据格式

数据文件的每一行是一个项目,Potato 从这个池子里采样出元组。

json
{"id": "sys_a", "text": "Le chat s'est assis sur le tapis."}
{"id": "sys_b", "text": "Le chat a assis sur le tapis."}
{"id": "sys_c", "text": "Le chat etait assis sur le mat."}
{"id": "sys_d", "text": "Le chat se tenait sur le tapis."}

元组生成会让整个设计保持平衡:每个项目出现的次数大致相同,需要相互比较的配对共同出现得足够频繁,分数才有可比性。设了 seed,这套分配就可以复现。

评分

评分在收集完成之后进行,从命令行运行:

bash
python -m potato.bws_scoring --config config.yaml --method counting

结果写到输出目录下的 bws_scores.tsv,除非 --output 指定了别的路径。参数一共三个:--config、--method 和 --output。

计数法

默认方法,也是最透明的一个。一个项目的分数,是它被选为最好的比例减去被选为最差的比例,所以分数落在 -1.0 到 +1.0 之间。它不需要额外依赖;如果你希望读者能自己手算一遍来核对,报这个方法。

bash
python -m potato.bws_scoring --config config.yaml --method counting --output scores.tsv

Bradley-Terry

在每个元组隐含的成对比较上拟合一个模型:最好的项目胜过元组里其余所有项目,其余所有项目又都胜过最差的那个。数据稀疏时,它比计数法处理得更好。

bash
python -m potato.bws_scoring --config config.yaml --method bradley_terry

Plackett-Luce

把每次判断看作两次选择:先从整个元组中选出最好的,再从剩下的项目中选出最差的,这正是 MaxDiff 对最优-最差判断如何做出的建模。它用最大似然为每个项目拟合一个效用值。2.10.2 之前,它拟合的是与 Bradley-Terry 相同的成对比较,返回的分数完全相同;如果你报告过它的结果,请用 2.10.2 或更高版本重新计算分数。

bash
python -m potato.bws_scoring --config config.yaml --method plackett_luce

选择方法

方法速度使用的信息适用场景
counting快最好和最差的计数探索性分析,以及希望别人容易核对的结果
bradley_terry中等隐含的成对比较通常的默认选择
plackett_luce较慢每一次最好和最差的选择,按做出的先后顺序对判断本身的似然模型

bradley_terry 需要 pip install choix。plackett_luce 用的是 scipy,Potato 已经自带安装。

一次评多个维度

在同一批项目上放两个 BWS 方案,每个元组就收集两次判断,代价是多点一对按钮,而不是把语料再跑一遍:

yaml
annotation_schemes:
  - annotation_type: bws
    name: fluency
    description: "Select the BEST and WORST by fluency"
    tuple_size: 4
    best_description: "Most Fluent"
    worst_description: "Least Fluent"
 
  - annotation_type: bws
    name: adequacy
    description: "Select the BEST and WORST by meaning preservation"
    tuple_size: 4
    best_description: "Most Accurate"
    worst_description: "Least Accurate"

输出里有什么

每个方案记录下被选为最好的项目和被选为最差的项目:

json
{
  "fluency": {"best": "sys_d", "worst": "sys_c"},
  "adequacy": {"best": "sys_a", "worst": "sys_c"}
}

分数不在这个文件里。它们是事后由 potato.bws_scoring 算出来的——正因为如此,你可以换一种方法给一项已经做完的研究重新评分,而不必重新收集任何数据。

监控

管理面板有一个 BWS 标签页,显示标注进度和当前的项目分数,这样你可以在决定停止收集之前,先看看排名有没有稳定下来。

完整示例

yaml
annotation_task_name: "MT System Ranking (BWS)"
task_dir: "."
 
data_files:
  - "data/mt_systems.jsonl"
 
item_properties:
  id_key: id
  text_key: text
 
bws_config:
  tuple_size: 4
  min_item_appearances: 8
  seed: 42
  scoring:
    method: bradley_terry
 
annotation_schemes:
  - annotation_type: bws
    name: overall_quality
    description: "Select the BEST and the WORST translation"
    tuple_size: 4
    best_description: "Best Translation"
    worst_description: "Worst Translation"
    sequential_key_binding: true
    label_requirement:
      required: true
 
output_annotation_dir: "annotation_output/"
export_annotation_format: jsonl
 
user_config:
  allow_all_users: true

先收集标注,再给它们评分:

bash
potato start config.yaml -p 8000
python -m potato.bws_scoring --config config.yaml --method bradley_terry

延伸阅读

有关实现细节,请参阅源文档。