Skip to content
教程閱讀約需 10 分鐘

影像比較與偏好任務

在 Potato 中構建影像比較任務:兩張影像並排顯示、一個偏好方案、隨機化的呈現順序,以及適用於更大候選池的排名設定。

Potato Team

本教程在 Potato 中搭建一個影像比較任務:兩張生成的影像並排顯示、一條五點偏好量表、針對各項標準的追問,以及當候選多於兩張時使用的排名和最佳-最差縮放法兩種變體。同一套構造也適用於 RLHF 偏好資料、壓縮與增強效果的比較,以及設計 A/B 測試。

比較任務讓標註者在兩張影像之間做選擇,而不是把一張影像放到某個量表上打分,這樣就不需要每位標註者對"5 分裡的 4 分"意味著什麼都有相同的校準。代價是,一組 n 張影像要得到完整的排序需要多得多的判斷次數,這正是文末排名和最佳-最差縮放法兩節要解決的問題。

資料檔案

每一行包含一個識別符號、兩張影像的路徑,以及標註者判斷時所需的任何上下文。Potato 從 item_properties 讀取欄位名,所以名字本身由你決定:

json
{"id": "pair_001", "prompt": "A sunset over mountains", "image_a": "/media/model_a_001.png", "image_b": "/media/model_b_001.png"}
{"id": "pair_002", "prompt": "A cat on a windowsill", "image_a": "/media/model_a_002.png", "image_b": "/media/model_b_002.png"}

讓兩張影像並排顯示

在 Potato 中,內容和問題是分開配置的。instance_display 塊規定標註者看到什麼,annotation_schemes 規定問他們什麼。兩個 image 欄位加上橫向佈局,就把兩張影像並排放在一起;zoomable 讓標註者能檢視在顯示尺寸下看不見的瑕疵:

yaml
instance_display:
  layout:
    direction: horizontal
    gap: 24px
  fields:
    - key: image_a
      type: image
      label: "Image A"
      display_options:
        max_height: 512
        zoomable: true
        alt_text: "First generated image"
    - key: image_b
      type: image
      label: "Image B"
      display_options:
        max_height: 512
        zoomable: true
        alt_text: "Second generated image"

展示和方案都配置好之後,標註者看到的是這樣:

兩張並排的影像,下方是一條偏好量表一個比較任務,兩個候選項都顯示出來,偏好問題在它們下方

偏好問題

五點量表能把強偏好和弱偏好區分開。當你要拿結果去訓練時,這一點很重要,因為標註者意見對半分的一對,與他們都判斷得很明確的一對,攜帶的訓練訊號並不相同。sequential_key_binding 把 1 到 5 這五個按鍵分配給五個選項,這決定了一次比較花四秒還是花十二秒。

yaml
annotation_schemes:
  - annotation_type: radio
    name: preference
    description: "Which image better matches the prompt?"
    labels:
      - name: "A is clearly better"
      - name: "A is slightly better"
      - name: "About equal"
      - name: "B is slightly better"
      - name: "B is clearly better"
    sequential_key_binding: true
    label_requirement:
      required: true

把這一段加到上面的配置裡。中間那個選項要寫成"About equal"而不是"No preference",因為這兩者描述的是不同的判斷,而標註者會把"沒有偏好"這個選項當成跳過按鈕來用。

位置偏差

標註者選左邊選項的次數比應有的更多,所以一張影像落在哪一側必須有變化。在構建資料檔案時就做隨機化:隨機取一半的行交換 image_a 和 image_b,並記錄每一行用的是哪種順序,分析之前再把記錄下來的判斷翻轉回去。Potato 的展示欄位按你列出的順序渲染,所以配置本身沒法替你做這件事。

文本比較則不同。pairwise 方案從條目資料的同一個欄位中取出兩個候選項,所以 randomize_order 可以對它們做置換,而且 Potato 會記錄每位標註者實際看到的順序。隨機種子由標註者、條目和方案共同匯出,正是這一點讓一個人對第一個位置的偏好在不同條目上落到不同的候選項上,而不是整項研究都偏向同一側。如果要比較的是文本,見成對比較文件。

針對具體標準提問

整體偏好只告訴你哪張影像勝出,不說明原因。在下面加上兩三個更窄的問題,能讓分歧變得可讀,因為在整體判斷上分裂的標註者,往往在提示詞契合度上意見一致,分歧出在美感上。

yaml
  - annotation_type: radio
    name: prompt_adherence
    description: "Which image better matches the prompt?"
    labels:
      - name: A
      - name: Tie
      - name: B
 
  - annotation_type: multiselect
    name: issues_a
    description: "Problems in Image A (select all that apply)"
    labels:
      - name: "Distorted faces or hands"
      - name: "Text rendering errors"
      - name: "Unnatural lighting"
      - name: "Missing elements from the prompt"
      - name: "Blurry or low quality"
      - name: "None"

把這些方案加到上面那一段裡。標準的列表要短。每多一個問題,都會乘以整項研究的規模:2,000 對樣本上的五項標準表單就是 10,000 次判斷,而每一項都需要在說明裡有自己的定義。

前後對比

增強、修復和編輯類任務用的是同樣的雙圖構造,只是兩側要具名而不是隨機化,因為哪一側是原圖本身就是問題的一部分:

yaml
instance_display:
  layout:
    direction: horizontal
    gap: 24px
  fields:
    - key: original
      type: image
      label: "Original"
      display_options:
        max_height: 512
        zoomable: true
    - key: enhanced
      type: image
      label: "Enhanced"
      display_options:
        max_height: 512
        zoomable: true
 
annotation_schemes:
  - annotation_type: radio
    name: enhancement_quality
    description: "How did the edit change the image?"
    labels:
      - name: "Clearly improved"
      - name: "Slightly improved"
      - name: "No noticeable change"
      - name: "Made worse"
    label_requirement:
      required: true
 
  - annotation_type: multiselect
    name: problems_introduced
    description: "Problems the edit introduced (select all that apply)"
    labels:
      - name: "Over-sharpening or halos"
      - name: "Color shift"
      - name: "Loss of detail"
      - name: "New artifacts"
      - name: "None"

把"編輯引入了哪些問題"和整體品質分開來問。一次把主體銳化、同時在它周圍加出光暈的編輯,在單一量表上會被讀成一次改進,而光暈恰恰是你需要知道的東西。

超過兩張影像

當每個條目有三到八個候選項時,用 gallery 欄位展示它們,並用 ranking 方案收集完整的排序。相簿從一個欄位裡取一份影像路徑列表,所以資料檔案裡存的是一個數組,而不是兩個獨立的鍵:

yaml
item_properties:
  id_key: id
  text_key: prompt
 
instance_display:
  fields:
    - key: prompt
      type: text
      label: "Prompt"
    - key: images
      type: gallery
      display_options:
        layout: grid
        columns: 2
        thumbnail_size: 320
        zoomable: true
 
annotation_schemes:
  - annotation_type: ranking
    name: preference_rank
    description: "Drag the image numbers to rank them from best at the top to worst at the bottom."
    labels:
      - "Image 1"
      - "Image 2"
      - "Image 3"
      - "Image 4"
    allow_ties: false
    label_requirement:
      required: true

labels 給相簿中的各個位置命名,所以列表裡每張影像要有一個條目,而且每個條目都需要有相同數量的候選項。排名適合小集合,因為標註者要一次性把每個候選項與其他所有候選項作比較。超過八個候選項左右,每個條目上的投入增長得比換回的資訊量更快。

更大的候選池

當候選池大到無法在一屏裡排完序時,就該用最佳-最差縮放法。標註者看到一個條目元組,通常是四個,只挑出其中最好和最差的,分數則在許多相互重疊的元組上計算得出:

yaml
  - annotation_type: bws
    name: quality
    description: "Select the BEST and the WORST image in this set."
    tuple_size: 4
    best_description: "Best"
    worst_description: "Worst"

把這個方案加到上面的配置裡。計分在收集完成之後進行,可以從管理後臺的 BWS 標籤頁或命令列執行,結果寫入輸出目錄下的 bws_scores.tsv:

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

counting 方法不需要額外依賴,是那條透明的基線。bradley_terry 會在隱含的成對比較上擬合模型,需要 pip install choix。plackett_luce 直接對每一次最好和最差的選擇建模,除了 Potato 本身不需要別的依賴。最佳-最差縮放法文件講的是在這一切之前必須先做的元組生成。

說明與注意力檢測

在說明裡講清楚"更好"在這個任務裡指什麼,否則標註者會自己補上定義,而你要到算一致性的時候才發現。annotation_instructions 會把這段文字放到每一個標註頁面上:

yaml
annotation_instructions: |
  Judge each pair on how well it matches the prompt first, then on visual
  quality, then on how appealing it looks. Zoom in before deciding: hands,
  text, and fine texture are where generation failures show up. Mark "About
  equal" only when you have looked at both and cannot separate them.

比較任務的注意力檢測就是那些勝負一目瞭然的影像對,它們從各自單獨的檔案混入佇列,因此不會進入你的資料集:

yaml
attention_checks:
  enabled: true
  items_file: "data/attention_checks.json"
  frequency: 20
  min_response_time: 3.0
  failure_handling:
    warn_threshold: 2
    block_threshold: 5

把這兩段都加到上面的配置裡。min_response_time 能抓住比較任務最容易出現的那種失效方式:標註者點選的速度快到根本來不及看第二張影像。

完整配置

這份配置執行的是一次 RLHF 偏好資料收集:每個條目兩份模型輸出、一條五點偏好量表、一個標準問題和一項置信度評分。每一對由三位標註者檢視,這足以報告一致性並取多數標籤。

yaml
annotation_task_name: "Generative Model Comparison"
task_description: "Compare two generated images against the prompt that produced them."
 
data_files:
  - data/model_outputs.json
 
item_properties:
  id_key: id
  text_key: prompt
 
instance_display:
  layout:
    direction: horizontal
    gap: 24px
  fields:
    - key: image_a
      type: image
      label: "Image A"
      display_options:
        max_height: 512
        zoomable: true
        alt_text: "First generated image"
    - key: image_b
      type: image
      label: "Image B"
      display_options:
        max_height: 512
        zoomable: true
        alt_text: "Second generated image"
 
annotation_instructions: |
  Judge each pair on prompt match first, then visual quality, then appeal.
  Zoom in before deciding. Mark "About equal" only when you cannot separate them.
 
annotation_schemes:
  - annotation_type: radio
    name: preference
    description: "Which image better represents the prompt?"
    labels:
      - name: "A is clearly better"
      - name: "A is slightly better"
      - name: "About equal"
      - name: "B is slightly better"
      - name: "B is clearly better"
    sequential_key_binding: true
    label_requirement:
      required: true
 
  - annotation_type: radio
    name: prompt_adherence
    description: "Which image matches the prompt more closely?"
    labels:
      - name: A
      - name: Tie
      - name: B
 
  - annotation_type: likert
    name: confidence
    description: "How confident are you in this judgment?"
    size: 5
    min_label: "Guessing"
    max_label: "Certain"
 
attention_checks:
  enabled: true
  items_file: "data/attention_checks.json"
  frequency: 20
  min_response_time: 3.0
  failure_handling:
    warn_threshold: 2
    block_threshold: 5
 
num_annotators_per_item: 3
output_annotation_dir: annotation_output/image-comparison/
export_annotation_format: jsonl

用 potato start config.yaml -p 8000 啟動,在瀏覽器裡開啟這個埠並登入,先確認影像對的渲染符合你的預期,再把連結發給別人。自己也標幾個條目,因為在配置裡讀起來很清楚的方案,往往到了第三對真實資料上就顯得含混。

輸出裡有什麼

Potato 會在 output_annotation_dir 下為每位標註者寫一份檔案,export_annotation_format 則額外在 <output_annotation_dir>/exports/<format>/ 下定期匯出。每條匯出記錄都寫明實例、標註者,以及每個方案所選的標籤:

json
{"instance_id": "pair_001", "user_id": "rater_01", "labels": {"preference": {"A is slightly better": "A is slightly better"}, "confidence": {"4": "4"}}, "spans": {}, "links": {}}

按 instance_id 把它與你的資料檔案連線起來,還原出哪一側是哪個模型產出的,並在做任何聚合之前撤銷你構建檔案時施加的左右交換。把這一步放到最後做,而不是放進標註配置裡,才能保留每位標註者實際看到了什麼的原始記錄。

下一步