跳至主要內容

指南

教學:以檢查和能看到影像的評審評估影像

評估一個產生影像的函式,這裡是一個同時撰寫說明文字的商品縮圖產生器。以無需模型的量測檢查縮圖,以一個能看到每張縮圖的模型評判說明文字,用一個人來量測這個評審,然後發布一次真正的修改。每一步都在本機上執行,無需模型也無需連網,另有一個可選步驟會換用真實的視覺模型。

您將建置什麼

一個商品目錄服務接收一張商品照片,儲存一張縮圖,並寫一行說明文字,說出商品的顏色。三個問題決定一個版本能否發布:

  • 每張縮圖是否符合目錄的要求:網頁格式,最多 256 乘 256 像素,最多 50 kB?這是一項量測,由 media_check 依據縮圖的位元組進行。
  • 每張縮圖是否仍然是商品的照片,而不是空白影像、裁切圖或另一件商品?image_matches_reference 透過感知雜湊把它與照片進行比較。
  • 每條說明文字是否說出了顧客看到的顏色?這需要眼睛,所以會向一個評分準則評審展示縮圖。

照片和縮圖留在專案資料夾中。系統以參照記錄它們(media/v1):每個檔案在哪裡、它的類型、大小與 sha256。案例、執行、評審和閘門等術語在核心概念中有定義;關於媒體的一切都在影像、音訊和影片中。

事前準備

  • Python 3.11 或更高版本,以及帶媒體額外套件的 Oloproof,該額外套件加入了用於比較影像的 Pillow:
python3 -m venv .venv
. .venv/bin/activate
pip install 'oloproof[media]'
  • 範例專案,它隨軟體套件一起提供。把它複製到一個新目錄並在那裡工作:
oloproof init --example media product-thumbnails
cd product-thumbnails
  • 為替身評審空出 8798 連接埠(如果不行,就在兩處同時修改)。

在「可選:真實的視覺模型」之前的每一步都是離線且確定性的:沒有 API 金鑰,沒有提供者帳號,沒有費用。

檔案

product-thumbnails/
  app.py                              the thumbnailer under test (baseline)
  app_v2.py                           the candidate change
  judge_server.py                     a stand-in vision judge speaking the OpenAI API on 127.0.0.1
  rubrics/caption_names_colour.md     the judge's rubric
  oloproof.yaml                       the suite
  release.yaml                        rules for a run
  compare.yaml                        a rule for a comparison
  data/products.jsonl                 20 cases
  photos/                             the 20 product photos the cases name
  make_photos.py                      how the photos were drawn
  labels/reviewer_verdicts.csv        one person's verdicts on the baseline's captions
  fill_labels.py                      copies those verdicts into a labelling sheet

在 product-thumbnails/ 中執行每條命令。

應用記錄它的影像

# app.py (excerpt)
@system(name="thumbnailer", version=VERSION, records=("media/v1",))
def describe(case: dict[str, Any]) -> dict[str, str]:
    photo_path = HERE / case["photo"]
    photo = Image.open(photo_path).convert("RGB")
    thumb = photo.copy()
    thumb.thumbnail((320, 320))
    out = HERE / "thumbs" / VERSION / photo_path.name
    out.parent.mkdir(parents=True, exist_ok=True)
    thumb.save(out)
    given = media_file(photo_path, role="input")
    made = media_file(out, width=thumb.width, height=thumb.height)
    current_case().media([given, made])
    centre = photo.getpixel((photo.width // 2, photo.height // 2))
    return {"caption": f"A {colour_name(centre)} product."}

media_file(path) 描述一個檔案:它的 file:// 位置、MIME 類型、大小與 sha256,並以檔名命名。role="input" 標記系統收到的照片;縮圖是輸出,即預設值。current_case().media(...) 把兩者記錄為一個 media/v1 產物,而 oloproof.yaml 中的 records: [media/v1] 宣告系統會記錄它。不會複製任何內容:檢查或評審稍後會讀回每個檔案,並拒絕位元組已不再與記錄相符的檔案。

這個基準有兩個缺陷。它的縮圖寬 320 像素,而它的說明文字說出的是照片中心像素的顏色,只要商品位於一側,這個像素就是背景。每個版本把它的縮圖寫在 thumbs/<version>/ 下,因此之後的執行絕不會覆寫之前的執行所記錄的檔案。

資料集

{"id": "p03", "input": {"photo": "photos/p03.png"}, "expected": {"photo": "photos/p03.png", "colour": "yellow", "shape": "triangle"}}

input 是系統接收的內容。expected.photo 是與縮圖進行比較的參考;colour 和 shape 是給閱讀案例的人看的,沒有評估器讀取它們。

選擇評估器

evaluators:
  # The catalogue's limits, measured from the thumbnail's bytes: a web format, at most 256 by
  # 256 pixels, at most 50 kB.
  - type: media_check
    criterion: thumbnail_fits
    kind: image
    formats: [png, jpeg, webp]
    max_width: 256
    max_height: 256
    max_bytes: 50000
  # The thumbnail is still the product's photo: a perceptual hash within 10 of 64 bits.
  - type: image_matches_reference
    criterion: same_photo
    reference_field: photo
    max_distance: 10
  # The caption, judged by a model that sees the thumbnail.
  - type: rubric_judge
    criterion: caption_names_colour
    provider: openai_compatible
    model: stand-in-vision
    base_url: http://127.0.0.1:8798/v1
    rubric_file: rubrics/caption_names_colour.md
    media: output

media_check 量測每張輸出影像:格式取自它的開頭位元組,尺寸取自它的檔頭,絕不取自系統所宣告的值。image_matches_reference 對縮圖和 expected.photo 指明的照片計算雜湊,當兩個雜湊在 64 個位元中最多相差 10 個時通過;縮放或重新壓縮只會讓雜湊變動幾個位元,另一張圖片則會讓它變動很多個。兩者都不呼叫模型,所以都從第一次執行起就作出決策。

media: output 把案例的輸出影像附加到評審的請求中,每張都在提示詞中給出名稱。評審在人的標籤顯示它與人一致之前不決定任何事情,所以第三條規則要等待這一點。

替身評審

評分準則評審把它的提示詞和附加的影像發送給一個模型,並讀回 {"pass": true|false, "rationale": "..."}。這裡的模型是 judge_server.py,它在 127.0.0.1 上使用同樣的 API,但它不是模型:它解碼附加的縮圖,找出除背景的白色和灰色之外最常見的顏色,並讓說出這個顏色的說明文字通過。在第二個終端機中啟動它並讓它保持執行:

python judge_server.py --port 8798

政策

version: 1
confidence_level: 0.95
block_on: [FAIL, INSUFFICIENT_EVIDENCE, MANUAL_REVIEW]
require_validated_evaluators: true
rules:
  - id: thumbnails-fit
    metric: thumbnail_fits
    kind: observed_count
    max_failures: 0
  - id: same-photo
    metric: same_photo
    kind: observed_count
    max_failures: 0
  - id: caption-floor
    metric: caption_names_colour
    min: 0.80

每張縮圖都必須符合要求,並且必須是它的照片。至少 80% 的說明文字必須說出正確的顏色,只有當區間的下界達到 80% 時才作出決策。

執行

oloproof run
Gate: BLOCK (exit 1)
│ thumbnails-fit │ thumbnail_fits       │ FAIL                  │ observed_failures_exceed_limit   │
│ same-photo     │ same_photo           │ PASS                  │ observed_failures_within_limit   │
│ caption-floor  │ caption_names_colour │ INSUFFICIENT_EVIDENCE │ evaluator_not_validated          │
│ thumbnail_fits       │ 0.0%     │ [0.0%, 16.9%]   │ 0 / 20 observed · 0 missing · 0 excluded  │
│ same_photo           │ 100.0%   │ [83.1%, 100.0%] │ 20 / 20 observed · 0 missing · 0 excluded │
│ caption_names_colour │ 60.0%    │ [36.0%, 80.9%]  │ 12 / 20 observed · 0 missing · 0 excluded │

沒有一張縮圖符合要求,每張縮圖都是它的照片,20 條說明文字中有 12 條說出了正確的顏色。評審的規則尚未決定,因為還沒有人量測過這個評審。

檢視失敗項

oloproof inspect RUN_ID --failures
p03
  output: {"caption": "A white product."}
  thumbnail_fits: failed
    measured: p03.png: width 320 px is above 256 px
  caption_names_colour: failed
    judge text, not verified: the product is yellow; the caption says 'a white product.'

檢查的理由是一項量測,標記為 measured;評審的理由是它自己的話,沒有人核對過。在 oloproof export RUN_ID 之後,照片和縮圖也會出現在工作台中案例頁面上每個結論的旁邊,從這個資料夾讀取;託管工作區讓您的瀏覽器從 oloproof collect 取得它們,自己從不持有它們(影像、音訊和影片)。

用一個人來量測評審

從執行的案例中隨機抽取一個樣本放進一張表,不帶評審的結論,讓一個人查看每張縮圖和說明文字,然後匯入他們的結論。這裡 fill_labels.py 代替那個人,從 labels/reviewer_verdicts.csv 複製他們的結論:

oloproof labels export RUN_ID --criterion caption_names_colour --sample 20 --local --out sample.csv
python fill_labels.py sample.csv
oloproof labels import sample.csv

然後對照這些標籤量測評審,並記錄是誰做的:

oloproof evaluators list
oloproof evaluators validate EVALUATOR_ID --by alice
caption_names_colour: sha256:53605dd6... is now VALIDATED
  agreement 100.0% [83.1%, 100.0%] · 20 of 20 labelled cases agreed · 0 labelled but not judged · kappa 1.000

能看到影像的評審與讀取文字的評審的量測方式完全相同。在託管工作區中,審查佇列向審查者展示每個案例的影像,由他們的瀏覽器從您的收集器取得(評審)。

oloproof gate RUN_ID --policy release.yaml
thumbnails-fit: FAIL (observed_failures_exceed_limit)
same-photo: PASS (observed_failures_within_limit)
caption-floor: INSUFFICIENT_EVIDENCE (interval_overlaps_threshold)
Gate: BLOCK (exit 1)

現在評審可以作出決策了,而 60% 不夠。

做一次真正的修改

app_v2.py 產生 256 像素的縮圖,並說出照片中除背景之外最常見的顏色,無論商品位於何處。把它複製到 app.py 上覆蓋,在 oloproof.yaml 的 system 下設定 version: main-colour,保持評審執行,然後:

oloproof run
Gate: ALLOW (exit 0)
│ thumbnails-fit │ thumbnail_fits       │ PASS  │ observed_failures_within_limit │
│ same-photo     │ same_photo           │ PASS  │ observed_failures_within_limit │
│ caption-floor  │ caption_names_colour │ PASS  │ lower_bound_meets_minimum      │

評審是經過驗證的同一個評估器版本,所以它的結論有效。

把候選與基準進行比較

oloproof compare CANDIDATE_RUN_ID BASELINE_RUN_ID --policy compare.yaml
thumbnail_fits: +100.0 points [+52.9, +100.0] · 20 paired · 0 missing · 0 excluded
caption_names_colour: +40.0 points [+1.8, +72.7] · 20 paired · 0 missing · 0 excluded
  captions-better  caption_names_colour  superiority  PASS  difference_above_zero

可選:真實的視覺模型

這一步離開了離線路徑。它需要一個能讀取影像的模型:本機可以用帶視覺模型的 Ollama(ollama pull qwen2.5vl),無需金鑰也無需費用;雲端可以用 provider: anthropic 或 openai 並帶上 api_key_env,按每張影像以及每個字詞計費。

# live_judge.yaml
type: rubric_judge
criterion: caption_names_colour
provider: openai_compatible
model: qwen2.5vl
base_url: http://localhost:11434/v1
rubric_file: rubrics/caption_names_colour.md
media: output
timeout_s: 300
oloproof evaluators try live_judge.yaml

這會用真實模型評判您的審查者已標註過的案例,並回報它的一致性,而不採用它。要採用它,就把它放進 oloproof.yaml,替換掉替身:新模型就是新的評估器版本,所以要像上面那樣對照標籤驗證它。

疑難排解

症狀原因與修復
caption_names_colour 全部缺失評判伺服器沒有執行,或不在 base_url 上;oloproof inspect RUN_ID --failures 會顯示原因。
image_matches_reference 停止執行並提到 Pillow安裝媒體額外套件:pip install 'oloproof[media]'。
一個案例因 media_unavailable 而缺失執行之後,某個已記錄的檔案被移動或刪除了。只要您保留一次執行,就保留它所記錄的檔案。
執行以 is not the recorded content 停止執行之後,某個已記錄的檔案被修改了。檢查或評審絕不會把不同的位元組當作已記錄的位元組來讀取;把每個版本的檔案寫到各自的位置。
驗證之後仍出現 evaluator_not_validated您修改了評審(模型、端點、連接埠、評分準則、media),產生了一個新版本。驗證那個版本。

侷限

  • 替身評審只是在數像素。它示範的是工作流程,而不是評判品質。
  • 感知雜湊讀取的是構圖與明暗,而不是顏色:它能把照片的縮圖與空白影像或不同的場景區分開,卻無法區分同一個攝影棚裡的兩件商品。有大片空白區域的平面圖形比照片更容易讓它變動。
  • 二十個標籤給出的一致性區間很寬。對於您所依賴的評審,請以隨機且盲測的方式標註更多。
  • 音訊和影片以同樣的方式運作,只是設定不同:見影像、音訊和影片。