Langsung ke konten

Panduan

Tutorial: gambar, dengan pemeriksaan dan juri yang melihatnya

Evaluasi fungsi yang membuat gambar, di sini pembuat thumbnail produk yang juga menulis keterangan (caption). Periksa thumbnail dengan pengukuran yang tidak memerlukan model, nilai keterangannya dengan model yang melihat setiap thumbnail, ukur juri itu terhadap seseorang, lalu rilis perubahan nyata. Setiap langkah berjalan di mesin ini tanpa model dan tanpa jaringan, dan sebuah langkah opsional menggantinya dengan model vision sungguhan.

Apa yang akan Anda bangun

Sebuah layanan katalog menerima foto produk, menyimpan thumbnail, dan menulis keterangan satu baris yang menyebut warna produk. Tiga pertanyaan menentukan apakah sebuah versi boleh dirilis:

  • Apakah setiap thumbnail sesuai dengan katalog: format web, paling besar 256 kali 256 piksel, paling besar 50 kB? Itu sebuah pengukuran, yang dilakukan media_check dari byte thumbnail.
  • Apakah setiap thumbnail masih merupakan foto produknya, dan bukan gambar kosong, potongan, atau produk lain? image_matches_reference membandingkannya dengan foto itu melalui perceptual hash.
  • Apakah setiap keterangan menyebut warna yang dilihat pembeli? Itu membutuhkan mata, sehingga juri rubrik ditunjukkan thumbnail-nya.

Foto dan thumbnail tetap berada di folder proyek. Sistem merekamnya berdasarkan referensi (media/v1): di mana setiap file berada, tipenya, ukurannya, dan sha256-nya. Istilah seperti kasus, eksekusi, juri, dan gerbang didefinisikan di Konsep inti; segala hal tentang media ada di Gambar, audio, dan video.

Prasyarat

  • Python 3.11 atau yang lebih baru, dan Oloproof dengan ekstra media-nya, yang menambahkan Pillow untuk membandingkan gambar:
python3 -m venv .venv
. .venv/bin/activate
pip install 'oloproof[media]'
  • Proyek contoh, yang disertakan dalam paket. Salin ke direktori baru dan bekerjalah di sana:
oloproof init --example media product-thumbnails
cd product-thumbnails
  • Port 8798 kosong untuk juri pengganti (ubah di kedua tempat jika tidak).

Setiap langkah hingga "Opsional: model vision sungguhan" bersifat offline dan deterministik: tanpa kunci API, tanpa akun penyedia, tanpa biaya.

File-filenya

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

Jalankan setiap perintah dari product-thumbnails/.

Aplikasi merekam gambarnya

# 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) mendeskripsikan sebuah file: lokasi file://-nya, tipe MIME, ukuran, dan sha256, dinamai menurut nama file-nya. role="input" menandai foto yang diberikan kepada sistem; thumbnail adalah keluaran, yang merupakan bawaan. current_case().media(...) merekam keduanya sebagai satu artefak media/v1, dan records: [media/v1] di oloproof.yaml mendeklarasikan bahwa sistem merekamnya. Tidak ada yang disalin: pemeriksaan atau juri membaca kembali setiap file nanti dan menolak file yang byte-nya tidak lagi cocok dengan rekamannya.

Baseline ini memiliki dua kesalahan. Thumbnail-nya selebar 320 piksel, dan keterangannya menyebut warna piksel tengah foto, yang merupakan latar belakang setiap kali produk berada di satu sisi. Setiap versi menulis thumbnail-nya di bawah thumbs/<version>/, sehingga eksekusi berikutnya tidak pernah menimpa file yang direkam eksekusi sebelumnya.

Dataset

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

input adalah apa yang diterima sistem. expected.photo adalah acuan yang dibandingkan dengan thumbnail; colour dan shape ada untuk orang yang membaca kasus itu, dan tidak ada evaluator yang membacanya.

Memilih evaluator

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 mengukur setiap gambar keluaran: format dari byte pembukanya, ukuran dari header-nya, tidak pernah dari apa yang dideklarasikan sistem. image_matches_reference meng-hash thumbnail dan foto yang disebut expected.photo, dan lolos ketika hash-nya berbeda paling banyak 10 dari 64 bit; pengubahan ukuran atau kompresi ulang menggeser hash beberapa bit, gambar lain menggesernya banyak bit. Keduanya tidak memanggil model, sehingga keduanya memutuskan sejak eksekusi pertama.

media: output melampirkan gambar keluaran kasus itu ke permintaan juri, masing-masing disebut namanya dalam prompt. Juri tidak memutuskan apa pun sampai label manusia menunjukkan bahwa juri itu sepakat dengan mereka, sehingga aturan ketiga menunggu hal itu.

Juri pengganti

Juri rubrik mengirim prompt dan gambar yang dilampirkan ke sebuah model dan membaca kembali {"pass": true|false, "rationale": "..."}. Di sini modelnya adalah judge_server.py, yang berbicara dengan API yang sama di 127.0.0.1 dan bukan model: server ini mendekode thumbnail yang dilampirkan, menemukan warnanya yang paling umum selain putih dan abu-abu latar belakang, dan meloloskan keterangan yang menyebut warna itu. Jalankan di terminal kedua dan biarkan tetap berjalan:

python judge_server.py --port 8798

Kebijakannya

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

Setiap thumbnail harus sesuai dan harus merupakan fotonya. Setidaknya 80% keterangan harus menyebut warna yang benar, yang diputuskan hanya ketika batas bawah interval melampaui 80%.

Jalankan

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 │

Tidak ada thumbnail yang sesuai, setiap thumbnail merupakan fotonya, dan 12 dari 20 keterangan menyebut warna yang benar. Aturan juri belum diputuskan karena belum ada yang mengukur juri itu.

Periksa kegagalannya

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.'

Alasan sebuah pemeriksaan adalah pengukuran, ditandai measured; alasan juri adalah kata-katanya sendiri, yang belum diperiksa siapa pun. Foto dan thumbnail juga muncul di samping setiap putusan di halaman kasus di workbench, setelah oloproof export RUN_ID, dibaca dari folder ini; ruang kerja yang di-hosting membuat browser Anda mengambilnya dari oloproof collect dan tidak pernah menyimpannya (Gambar, audio, dan video).

Ukur juri terhadap seseorang

Ambil sampel acak dari kasus-kasus eksekusi ke dalam sebuah lembar, tanpa putusan juri, minta seseorang melihat setiap thumbnail dan keterangannya, lalu impor apa yang dikatakannya. Di sini fill_labels.py menggantikan orang itu, dengan menyalin putusannya dari 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

Lalu ukur juri terhadap label-label itu, dan catat siapa yang melakukannya:

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

Juri yang melihat gambar diukur persis seperti juri yang membaca teks. Di ruang kerja yang di-hosting, antrean tinjauan menunjukkan gambar setiap kasus kepada peninjau, diambil oleh browser mereka dari kolektor Anda (Juri).

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)

Juri sekarang boleh memutuskan, dan 60% tidak cukup.

Lakukan perubahan nyata

app_v2.py membuat thumbnail 256 piksel dan menyebut warna foto yang paling umum selain latar belakang, di mana pun produknya berada. Salin menimpa app.py, tetapkan version: main-colour di bawah system di oloproof.yaml, biarkan juri tetap berjalan, lalu:

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      │

Juri adalah versi evaluator yang sama yang telah divalidasi, sehingga putusannya dihitung.

Bandingkan kandidat dengan baseline

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

Opsional: model vision sungguhan

Langkah ini meninggalkan jalur offline. Langkah ini memerlukan model yang dapat membaca gambar: secara lokal, Ollama dengan model vision (ollama pull qwen2.5vl), tanpa kunci dan tanpa biaya; di cloud, provider: anthropic atau openai dengan api_key_env, ditagih per gambar sebagaimana per kata.

# 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

Perintah itu menilai kasus-kasus yang diberi label oleh peninjau Anda dengan model sungguhan dan melaporkan kesepakatannya, tanpa mengadopsinya. Untuk mengadopsinya, masukkan ke oloproof.yaml menggantikan juri pengganti: model baru adalah versi evaluator baru, sehingga validasi terhadap label seperti di atas.

Pemecahan masalah

GejalaPenyebab dan perbaikan
caption_names_colour semuanya hilangServer juri tidak berjalan atau tidak berada di base_url; oloproof inspect RUN_ID --failures menunjukkan alasannya.
image_matches_reference menghentikan eksekusi, dengan menyebut PillowPasang ekstra media: pip install 'oloproof[media]'.
Sebuah kasus hilang dengan media_unavailableFile yang direkam dipindahkan atau dihapus setelah eksekusi. Simpan file yang direkam sebuah eksekusi selama Anda menyimpan eksekusi itu.
Eksekusi berhenti dengan is not the recorded contentFile yang direkam diubah setelah eksekusi. Pemeriksaan atau juri tidak pernah membaca byte yang berbeda sebagai byte yang direkam; tulis file setiap versi ke tempatnya sendiri.
evaluator_not_validated setelah Anda memvalidasiAnda mengubah juri (model, endpoint, port, rubrik, media) dan membuat versi baru. Validasi versi itu.

Keterbatasan

  • Juri pengganti menghitung piksel. Juri itu mendemonstrasikan alur kerja, bukan kualitas penilaian.
  • Perceptual hash membaca tata letak dan cahaya, bukan warna: hash ini membedakan thumbnail foto itu dari gambar kosong atau adegan yang berbeda, tetapi tidak membedakan satu produk dari produk lain di studio yang sama. Grafik datar dengan area kosong yang luas menggesernya lebih banyak daripada foto.
  • Dua puluh label memberi interval kesepakatan yang lebar. Beri label lebih banyak, secara acak dan buta, untuk juri yang Anda andalkan.
  • Audio dan video bekerja dengan cara yang sama dengan pengaturan lain: lihat Gambar, audio, dan video.