跳到主要内容

指南

教程:用检查和能看到图像的评判模型评估图像

评估一个生成图像的函数,这里是一个同时编写说明文字的商品缩略图生成器。用无需模型的度量检查缩略图,用一个能看到每张缩略图的模型评判说明文字,用一个人来度量这个评判模型,然后发布一次真正的修改。每一步都在本机上运行,无需模型也无需联网,另有一个可选步骤会换用真实的视觉模型。

你将构建什么

一个商品目录服务接收一张商品照片,保存一张缩略图,并写一行说明文字,说出商品的颜色。三个问题决定一个版本能否发布:

  • 每张缩略图是否符合目录的要求:网页格式,最多 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),产生了一个新版本。验证那个版本。

局限

  • 替身评判模型只是数像素。它演示的是工作流程,而不是评判质量。
  • 感知哈希读取的是布局和明暗,而不是颜色:它能把照片的缩略图与空白图像或不同的场景区分开,却无法区分同一个摄影棚里的两件商品。有大片空白区域的平面图形比照片更容易让它变动。
  • 二十个标签给出的一致性区间很宽。对于你所依赖的评判模型,请以随机且盲测的方式标注更多。
  • 音频和视频以同样的方式工作,只是设置不同:见图像、音频和视频。