Hướng dẫn
Hướng dẫn: hình ảnh, với các kiểm tra và một giám khảo nhìn thấy chúng
Đánh giá một hàm tạo ra hình ảnh, ở đây là một bộ tạo ảnh thu nhỏ (thumbnail) sản phẩm cũng viết một dòng chú thích. Kiểm tra các thumbnail bằng những phép đo không cần mô hình, chấm các chú thích bằng một mô hình nhìn thấy từng thumbnail, đo giám khảo đó so với một người, rồi phát hành một thay đổi thật. Mọi bước đều chạy trên máy này mà không cần mô hình và không cần mạng, và một bước tùy chọn thay vào một mô hình thị giác thật.
Bạn sẽ xây dựng gì
Một dịch vụ danh mục nhận một ảnh sản phẩm, lưu một thumbnail và viết một chú thích một dòng nêu màu của sản phẩm. Ba câu hỏi quyết định một phiên bản có được phát hành hay không:
- Mỗi thumbnail có phù hợp với danh mục không: một định dạng web, tối đa 256 x 256 điểm ảnh, tối đa 50 kB? Đó là một phép đo, do media_check thực hiện từ các byte của thumbnail.
- Mỗi thumbnail có còn là ảnh của sản phẩm, chứ không phải một hình trống, một phần bị cắt hay một sản phẩm khác không? image_matches_reference so sánh nó với ảnh gốc bằng perceptual hash.
- Mỗi chú thích có nêu màu mà người mua nhìn thấy không? Việc đó cần có mắt, nên một giám khảo rubric được cho xem thumbnail.
Ảnh và thumbnail vẫn nằm trong thư mục dự án. Hệ thống ghi lại chúng theo tham chiếu (media/v1): mỗi tệp ở đâu, kiểu, dung lượng và sha256 của nó. Các thuật ngữ như trường hợp, lần chạy, giám khảo và cổng được định nghĩa trong Khái niệm cốt lõi; mọi điều về đa phương tiện nằm trong Hình ảnh, âm thanh và video.
Điều kiện tiên quyết
- Python 3.11 trở lên, và Oloproof với phần bổ sung media, thứ thêm Pillow để so sánh hình ảnh:
python3 -m venv .venv
. .venv/bin/activate
pip install 'oloproof[media]'- Dự án ví dụ, đi kèm với gói. Sao chép nó vào một thư mục mới và làm việc ở đó:
oloproof init --example media product-thumbnails
cd product-thumbnails- Cổng 8798 còn trống cho giám khảo thay thế (nếu không, hãy đổi nó ở cả hai nơi).
Mọi bước cho tới "Tùy chọn: một mô hình thị giác thật" đều ngoại tuyến và tất định: không khóa API, không tài khoản nhà cung cấp, không chi phí.
Các tệp
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 sheetChạy mọi lệnh từ product-thumbnails/.
Ứng dụng ghi lại hình ảnh của nó
# 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) mô tả một tệp: vị trí file://, kiểu MIME, dung lượng và sha256 của nó, được đặt tên theo tên tệp. role="input" đánh dấu ảnh mà hệ thống được cung cấp; thumbnail là một đầu ra, là giá trị mặc định. current_case().media(...) ghi lại cả hai dưới dạng một artifact media/v1, và records: [media/v1] trong oloproof.yaml khai báo rằng hệ thống ghi lại nó. Không có gì được sao chép: một kiểm tra hoặc một giám khảo đọc lại từng tệp về sau và từ chối một tệp mà các byte không còn khớp với bản ghi.
Đường cơ sở này có hai lỗi. Các thumbnail của nó rộng 320 điểm ảnh, và chú thích của nó nêu màu của điểm ảnh ở chính giữa bức ảnh, vốn là phông nền mỗi khi sản phẩm nằm lệch về một bên. Mỗi phiên bản ghi thumbnail của nó vào thumbs/<version>/, nên một lần chạy sau không bao giờ ghi đè lên các tệp mà một lần chạy trước đã ghi lại.
Tập dữ liệu
{"id": "p03", "input": {"photo": "photos/p03.png"}, "expected": {"photo": "photos/p03.png", "colour": "yellow", "shape": "triangle"}}input là thứ hệ thống nhận được. expected.photo là hình tham chiếu mà thumbnail được so sánh với; colour và shape có mặt cho một người đọc trường hợp, và không bộ đánh giá nào đọc chúng.
Chọn các bộ đánh giá
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 đo từng hình ảnh đầu ra: định dạng từ các byte mở đầu của nó, kích thước từ phần header của nó, không bao giờ từ những gì hệ thống đã khai báo. image_matches_reference tính hash của thumbnail và của ảnh mà expected.photo nêu ra, và đạt khi các hash khác nhau tối đa 10 trên 64 bit của chúng; việc đổi kích thước hay nén lại làm hash lệch vài bit, một bức ảnh khác làm nó lệch nhiều bit. Không cái nào gọi mô hình, nên cả hai đều quyết định ngay từ lần chạy đầu tiên.
media: output đính kèm các hình ảnh đầu ra của trường hợp vào yêu cầu gửi tới giám khảo, mỗi hình được nêu tên trong prompt của nó. Một giám khảo không quyết định điều gì cho tới khi nhãn của con người cho thấy nó đồng ý với họ, nên quy tắc thứ ba phải chờ điều đó.
Giám khảo thay thế
Một giám khảo rubric gửi prompt và các hình ảnh đính kèm tới một mô hình và đọc lại {"pass": true|false, "rationale": "..."}. Ở đây mô hình là judge_server.py, nói cùng API đó trên 127.0.0.1 và không phải là một mô hình: nó giải mã thumbnail được đính kèm, tìm màu phổ biến nhất của thumbnail ngoài màu trắng và xám của phông nền, và cho qua một chú thích nêu đúng màu đó. Khởi động nó trong một terminal thứ hai và để nó chạy:
python judge_server.py --port 8798Chính sách
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.80Mọi thumbnail phải phù hợp và phải là ảnh của nó. Ít nhất 80% chú thích phải nêu đúng màu, chỉ được quyết định khi cận dưới của khoảng vượt qua 80%.
Chạy
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 │Không thumbnail nào phù hợp, mọi thumbnail đều là ảnh của nó, và 12 trên 20 chú thích nêu đúng màu. Quy tắc của giám khảo chưa được quyết định vì chưa ai đo giám khảo đó.
Xem xét các thất bại
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.'Lý do của một kiểm tra là một phép đo, được đánh dấu measured; lý do của một giám khảo là lời của chính nó, mà chưa ai kiểm tra. Ảnh và thumbnail cũng xuất hiện bên cạnh mỗi phán quyết trên trang của trường hợp trong workbench, sau oloproof export RUN_ID, được đọc từ thư mục này; một không gian làm việc được lưu trữ để trình duyệt của bạn tải chúng từ oloproof collect và không bao giờ giữ chúng (Hình ảnh, âm thanh và video).
Đo giám khảo so với một người
Rút một mẫu ngẫu nhiên các trường hợp của lần chạy vào một bảng tính, không có các phán quyết của giám khảo, nhờ một người xem từng thumbnail và chú thích, rồi nhập vào những gì họ đã nói. Ở đây fill_labels.py đóng vai người đó, sao chép các phán quyết của họ từ 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.csvSau đó đo giám khảo so với các nhãn đó, và ghi lại ai đã làm việc này:
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.000Một giám khảo nhìn thấy hình ảnh được đo giống hệt như một giám khảo đọc văn bản. Trong một không gian làm việc được lưu trữ, hàng đợi xem xét hiển thị hình ảnh của mỗi trường hợp cho người xem xét, được trình duyệt của họ tải từ collector của bạn (Giám khảo).
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)Giờ giám khảo đã được phép quyết định, và 60% là không đủ.
Thực hiện một thay đổi thật
app_v2.py tạo thumbnail 256 điểm ảnh và nêu màu phổ biến nhất của bức ảnh ngoài phông nền, bất kể sản phẩm nằm ở đâu. Sao chép nó đè lên app.py, đặt version: main-colour dưới system trong oloproof.yaml, giữ giám khảo tiếp tục chạy, và:
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 │Giám khảo là cùng phiên bản bộ đánh giá đã được xác thực, nên các phán quyết của nó được tính.
So sánh ứng viên với đường cơ sở
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_zeroTùy chọn: một mô hình thị giác thật
Bước này rời khỏi lộ trình ngoại tuyến. Nó cần một mô hình đọc được hình ảnh: cục bộ là Ollama với một mô hình thị giác (ollama pull qwen2.5vl), không khóa và không chi phí; trên đám mây là provider: anthropic hoặc openai với api_key_env, tính phí theo mỗi hình ảnh cũng như theo mỗi từ.
# 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.yamlLệnh đó chấm các trường hợp mà người xem xét của bạn đã gán nhãn bằng mô hình thật và báo cáo mức đồng thuận của nó, mà không áp dụng nó. Để áp dụng nó, hãy đặt nó vào oloproof.yaml thay cho giám khảo thay thế: một mô hình mới là một phiên bản bộ đánh giá mới, nên hãy xác thực nó so với các nhãn như trên.
Khắc phục sự cố
| Triệu chứng | Nguyên nhân và cách sửa |
|---|---|
| caption_names_colour thiếu toàn bộ | Máy chủ giám khảo không chạy hoặc không ở base_url; oloproof inspect RUN_ID --failures cho thấy lý do. |
| image_matches_reference dừng lần chạy, nêu tên Pillow | Cài phần bổ sung media: pip install 'oloproof[media]'. |
| Một trường hợp bị thiếu với media_unavailable | Một tệp đã ghi lại bị di chuyển hoặc bị xóa sau lần chạy. Hãy giữ các tệp mà một lần chạy đã ghi lại chừng nào bạn còn giữ lần chạy đó. |
| Một lần chạy dừng với is not the recorded content | Một tệp đã ghi lại bị thay đổi sau lần chạy. Một kiểm tra hay giám khảo không bao giờ đọc các byte khác như thể chúng là các byte đã ghi lại; hãy ghi các tệp của mỗi phiên bản vào chỗ riêng của nó. |
| evaluator_not_validated sau khi bạn đã xác thực | Bạn đã thay đổi giám khảo (mô hình, endpoint, cổng, rubric, media) và tạo ra một phiên bản mới. Hãy xác thực phiên bản đó. |
Giới hạn
- Giám khảo thay thế đếm điểm ảnh. Nó minh họa quy trình làm việc, không phải chất lượng chấm.
- Perceptual hash đọc bố cục và ánh sáng, không đọc màu sắc: nó phân biệt một thumbnail của bức ảnh với một hình trống hoặc một cảnh khác, chứ không phân biệt một sản phẩm với một sản phẩm khác trong cùng một studio. Đồ họa phẳng có những vùng trống lớn làm nó dịch chuyển nhiều hơn so với ảnh chụp.
- Hai mươi nhãn cho một khoảng đồng thuận rộng. Hãy gán nhãn nhiều hơn, một cách ngẫu nhiên và mù, cho một giám khảo mà bạn dựa vào.
- Âm thanh và video hoạt động theo cùng cách với các thiết lập khác: xem Hình ảnh, âm thanh và video.