指南
教學:以檢查和能看到影像的評審評估影像
評估一個產生影像的函式,這裡是一個同時撰寫說明文字的商品縮圖產生器。以無需模型的量測檢查縮圖,以一個能看到每張縮圖的模型評判說明文字,用一個人來量測這個評審,然後發布一次真正的修改。每一步都在本機上執行,無需模型也無需連網,另有一個可選步驟會換用真實的視覺模型。
您將建置什麼
一個商品目錄服務接收一張商品照片,儲存一張縮圖,並寫一行說明文字,說出商品的顏色。三個問題決定一個版本能否發布:
- 每張縮圖是否符合目錄的要求:網頁格式,最多 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: outputmedia_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 runGate: 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 --failuresp03
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 alicecaption_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.yamlthumbnails-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 runGate: 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.yamlthumbnail_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: 300oloproof 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),產生了一個新版本。驗證那個版本。 |
侷限
- 替身評審只是在數像素。它示範的是工作流程,而不是評判品質。
- 感知雜湊讀取的是構圖與明暗,而不是顏色:它能把照片的縮圖與空白影像或不同的場景區分開,卻無法區分同一個攝影棚裡的兩件商品。有大片空白區域的平面圖形比照片更容易讓它變動。
- 二十個標籤給出的一致性區間很寬。對於您所依賴的評審,請以隨機且盲測的方式標註更多。
- 音訊和影片以同樣的方式運作,只是設定不同:見影像、音訊和影片。