Skip to content

Контракты движка разметки

Шесть видов контрактов, которые движок разметки проверяет по человеческим меткам, от селективного риска и интервалов PPI до теста альтернативного разметчика.

Контракт представляет собой утверждение о готовых метках, которое движок разметки проверяет по человеческим меткам на случайной выборке. Каждый контракт называет одну схему разметки (schema) и имеет confidence, по умолчанию 0.9, то есть вероятность того, что утверждение верно. Значит, при confidence 0.9 выполненный контракт ошибочен с вероятностью 10%.

Шесть видов контрактов делают разные утверждения и подходят для разных исследований:

ВидУтверждениеДля чего
selective_riskНе более error принятых машинных меток ошибочныНаборы данных и обучающие наборы, где каждая метка должна быть пригодна
ppiИнтервал для распространённости каждой метки (или среднего балла), корректный при любом качестве машиныСообщение долей, например «12% постов токсичны [10,5; 13,6]»
no_worse_than_humanМашина согласуется с людьми не хуже, чем человекЗамена разметчиков или LLM-судьи
budget_firstЧто можно получить за бюджет человеческих метокПланирование до трат
distributionalРаспределение меток машины близко к тому, как расходятся людиСубъективные задачи, где разногласие само является данными
threshold_legacyНикакого, принимает выше фиксированной уверенностиТолько переведённые конфигурации

Ни один из этих контрактов не утверждает, что в метках нет ошибок. selective_risk ограничивает долю ошибок среди принятых меток, а каждый контракт, проверяющий машины, сравнивает их с людьми, которые разметили выборку.

Статус контракта

Каждый контракт имеет один из пяти статусов, которые показывают админ-страница, potato label status и сертификат:

СтатусЗначение
metТест пройден на текущем каскаде.
insufficientЧеловеческих меток пока недостаточно, с оценкой, сколько ещё нужно. Если эта оценка больше, чем осталось объектов для выборки в корпусе, статус сообщает, что цель на этом корпусе недостижима.
infeasibleМеток достаточно, но ни одно правило принятия не проходит, например потому, что уверенность машины не отделяет верные ответы от неверных.
violatedАудиты принятых меток показывают, что доля ошибок выше цели (при features.drift, включённом по умолчанию).
staleС момента последнего теста изменились источник, промпт или схема. Принятые метки ждут в pending_recert.

Машинная метка принимается, только если каждый контракт selective_risk и threshold_legacy на её схеме выполнен и принимает её. Контракты задают требования к меткам, поэтому невыполненный контракт блокирует принятие, а не пропускается, и статус, админ-страница и сертификат называют контракт, который не выполнен. Если ни одно правило не может действовать и больше ничего не отбирается (контракт невыполним, калибровочная выборка завершена или ничего не удалось спрогнозировать), объекты, у которых всё ещё нет метки, уходят людям в пределах budgets.human_labels.

Части dev и cert

Движок держит метки, которые выбирают правило, отдельно от меток, которые его проверяют. Калибровочные запросы отбираются случайно с записанной вероятностью включения, всегда слепые, и каждый случайно попадает в одну из двух частей:

  • dev (humans.dev_fraction, по умолчанию половина) обучает калибраторы и выбирает пороги;
  • cert проверяет выбранное правило, и больше её ничто не читает.

Попытки

Каждый новый тест контракта идёт на более строгом уровне, поэтому повторное тестирование по мере поступления меток не завышает доверие. Попытка номер k получает (1 - confidence) * 6 / (pi^2 k^2) бюджета ошибки, что в сумме по всем возможным попыткам даёт весь бюджет, а первая попытка получает 61% от него.

  • Попытки считаются для каждого контракта по всем каскадам, на которых он тестировался, и переименованный контракт сохраняет попытки своего прежнего id. Перебор промптов или моделей до первого успеха каждый раз стоит одну попытку. Возврат к прежнему каскаду тоже считается новой попыткой, и его прежний результат не восстанавливается.
  • Повторный тест на тех же данных не является новой попыткой. Те же cert-метки, те же предсказания и то же правило, выбранное на dev, возвращают сохранённый результат. Тест, который не удалось провести, например потому что в каком-то классе нет cert-меток, ничего не тратит.
  • potato label certify проверяет новые cert-метки и новые предсказания сразу, как и эквиваленты в REST, MCP и SDK. Если изменились только dev-метки, команда возвращает сохранённый результат с примечанием, что dev теперь выбирает другое правило, а --retest проверяет это правило на следующей попытке.
  • Планировщик сертифицирует сам по мере поступления меток. Выполненный контракт selective_risk остаётся выполненным для своего каскада, и за ним следят аудиты. Невыполненный контракт проверяется снова, когда его метки вырастут на четверть, и не меньше чем на 10. Результаты ppi, distributional и no_worse_than_human являются оценками, а не тестами с исходом «прошёл/не прошёл», поэтому планировщик переоценивает их на тех же рубежах и ещё раз, когда калибровочная выборка завершена.
  • Исправленная cert-метка считается новыми данными. Когда разметчик меняет свой ответ, правка стоит не больше новой метки. Если правка возвращает состояние, которое уже проверялось, возвращается результат того теста. Если ни один из тестов сохранённого результата не видит после правки больше ошибок, результат и его попытка остаются в силе. Иначе планировщик ждёт, пока метки вырастут на четверть от первой правки, чтобы серия исправлений стала одним тестом, а принятые метки тем временем остаются принятыми с примечанием на контракте. Когда новых меток больше не будет, он проверяет сразу, а явный вызов certify проверяет на следующей попытке.

Метки, которые люди поставили, увидев машинный ответ (запросы на проверку), и метки, поставленные вне запросов движка, никогда ничего не сертифицируют и не оценивают. На них можно обучить дистиллированную модель.

selective_risk

selective_risk ограничивает долю ошибочных среди принятых машинных меток. Ключ error задаёт наибольшую допустимую долю ошибок среди принятых меток и обязателен:

yaml
- id: accuracy
  kind: selective_risk
  schema: sentiment
  error: 0.05          # required: largest tolerated error among accepted labels
  confidence: 0.9
  per_class: true      # test each predicted class separately
  tolerance: 0.5       # graded families only: loss at or below this counts as correct

Правилом служит порог уверенности для каждого уровня каскада, а с per_class и для каждого предсказанного класса. Для всех уровней, кроме последнего, порог выбирается на dev и один раз проверяется на cert. Последний уровень перебирает пороги от выбранного на dev ко всё более мягким, проверяя каждый по очереди и останавливаясь на первом непрошедшем. Эта процедура называется fixed-sequence Learn-then-Test (Angelopoulos et al., 2025). Каждый тест является односторонним биномиальным тестом гипотезы о том, что ошибка среди принятых меток выше error, а уровень делится между уровнями каскада и классами. Не прошедший уровень ничего не принимает, и его объекты уходят людям.

Что считается «верным», зависит от семейства схемы. Функция потерь для каждого семейства приведена в таблице, и метка считается верной, если её потеря не больше tolerance:

Семейство схемПотеряtolerance по умолчанию
Один из N (radio, select)0, если метка та же0
Порядковые (likert)расстояние по шкале / (число делений - 1)0
Непрерывные (slider, number)расстояние / (max - min)0.05
Многометочные (multiselect)1 - коэффициент Жаккара0
Области изображения (image_annotation)1 - средний IoU по сопоставленным фигурам0.5
Сегменты аудио и видео, temporal_grounding, tiered_annotation1 - средний временной IoU0.5
3D-объекты (spatial_annotation)1 - средний 3D IoU0.5
Трассы агентов (process_reward, trajectory_eval, ...)доля общих шагов, размеченных по-разному0

Потери для областей, сегментов, 3D-объектов и трасс те же, что использует отчёт о согласованности на /admin/iaa, так что машина оценивается относительно человека так же, как два человека оцениваются друг относительно друга. У свободного текста и попарных схем семейства пока нет, и сертификат указывает их как неподдерживаемые.

Тесту нужно, чтобы каждая проверяемая группа cert-объектов была равномерной выборкой. Группа, объекты которой отбирались с неравными вероятностями, не проверяется, и результат её называет.

На последнем уровне порог выбирается на dev, только если ошибка на dev не больше половины цели и ожидается достаточно cert-меток, чтобы пройти первый тест. Прогноз того, сколько ещё меток нужно, учитывает только пороги, которые можно выбрать таким образом. Если в cert уже есть метки, которые запрашивал прогноз, а тест всё равно не проходит, следующий прогноз использует долю ошибок, которую показывают dev и cert вместе, а не одна dev. Если наименьшая ошибка на dev ниже цели, но выше её половины, ничего не проверяется и ничего не прогнозируется. Результат сообщает об этом и называет ошибку, поскольку больше меток помогут, только если ошибка окажется ниже.

metrics.accepted_by_tier показывает покрытие правила по всей совокупности, включая калибровочные объекты, и не является числом выданных машинных меток. Их считает строка сертификата «Machine labels accepted, by source».

Потеря для трасс сравнивает только шаги, размеченные обеими сторонами, поэтому человек, отметивший часть шагов, сравнивается по этим шагам. Шаги, которые никто не разметил, не проверяются, и человек, разметивший только шаг 0 и согласившийся там с судьёй, считается согласным со всей меткой трассы. Если проверить нужно каждый шаг, просите людей размечать каждый шаг.

ppi

ppi сообщает интервал для распространённости каждой метки или для среднего балла с поправкой на ошибки машины:

yaml
- id: prevalence
  kind: ppi
  schema: sentiment
  estimand: prevalence   # or mean, for numeric scores
  by: language           # optional: an interval per value of this item field
  confidence: 0.95
  max_width: 0.05        # optional: met once every interval is at most this wide

Prediction-powered inference (Angelopoulos et al., 2023) корректирует оценку машины по её ошибке на выборке, размеченной людьми. Интервал корректен при любом качестве машины. Хорошая машина делает его уже, чем могли бы одни люди, а плохая не делает шире. При равных вероятностях включения движок использует PPI++ с подобранным весом, а при неравных (стратифицированная выборка или выборка по неопределённости) разностную оценку Хорвица-Томпсона. Используется та машинная метка, которую выдал бы каскад. Поле by копируется из объектов при их регистрации. Без max_width контракт выполнен, как только у него достаточно меток для отчёта.

no_worse_than_human

no_worse_than_human спрашивает, согласуется ли машина с людьми не реже, чем человек:

yaml
- id: replaces-a-person
  kind: no_worse_than_human
  schema: verdict
  epsilon: 0.1

Контракт проводит тест альтернативного разметчика (Calderon et al., 2025). Исключая по очереди каждого человека, он проверяет, согласуется ли машина с остальными людьми не реже, чем исключённый человек, с допуском epsilon. Машина проходит тест, если выигрывает хотя бы у половины людей, при контроле доли ложных открытий по людям.

Тесту нужны объекты, размеченные несколькими людьми. Задайте humans.overlap (долю выборки, размечаемую больше одного раза) и humans.overlap_k (число людей на объект, не меньше 3), а самому тесту нужны как минимум 3 разметчика по 30 объектов у каждого. Пока их нет, контракт остаётся insufficient. Используйте этот контракт, когда фиксированная цель по точности измеряла бы в основном шум разметчиков. Если два внимательных человека согласны в 85% случаев, ни про одного разметчика нельзя показать, что он «прав» на 95%.

budget_first

budget_first прогнозирует, что даст бюджет человеческих меток, до того как вы его потратите:

yaml
- id: plan
  kind: budget_first
  schema: sentiment
  human_labels: 500      # or human_usd, priced at the crowd reward

Прогноз строится по уже собранным dev-меткам. Для каждой целевой доли ошибок на сетке он показывает, какую долю корпуса машинам можно сертифицировать для разметки при этом бюджете и насколько широким был бы каждый интервал распространённости. Контракт получает статус met, как только dev-меток хватает для прогноза. О самих метках он ничего не обещает: это делают selective_risk и ppi, когда метки собраны.

distributional

distributional сравнивает распределение меток машины с тем, как расходятся люди, в субъективных задачах, где люди расходятся по причинам, которые должны остаться в данных:

yaml
- id: disagreement
  kind: distributional
  schema: offensiveness
  max_distance: 0.15     # optional
  groups_by: age_band    # optional: a prestudy question whose answer groups the annotators

У каждого объекта, размеченного несколькими людьми (humans.distribution_k, по умолчанию 5), есть распределение человеческих меток. Контракт сообщает среднее расстояние Йенсена-Шеннона между распределением машины и распределением людей с бутстреп-интервалом. Он также сообщает потолок шума, то есть то же расстояние между двумя случайными половинами людей на каждом объекте. Машина, которая ближе к людям, чем люди друг к другу, близка настолько, насколько позволяют данные.

Распределением машины служат её вероятности там, где источник их даёт (модели принятия решений, k_sample, logprob), а иначе её метка в виде one-hot вектора. Контракт выполнен, когда верхний конец интервала не больше max_distance, а без max_distance когда есть 20 объектов, размеченных 3 или более людьми. Если нижний конец интервала выше max_distance, контракт получает статус infeasible, поскольку новые объекты лишь сужают интервал вокруг слишком большого расстояния.

С groups_by те же числа сообщаются по группам разметчиков. Распределение группы на объекте строится только по людям этой группы, которых определяют по ответам на названный вопрос перед исследованием, а если на этот вопрос никто не ответил, контракт сообщает об этом.

threshold_legacy

threshold_legacy принимает машинные метки выше фиксированной уверенности и не проводит статистического теста:

yaml
- id: old-rule
  kind: threshold_legacy
  schema: sentiment
  threshold: 0.8

potato label migrate-config пишет этот контракт для icl_labeling с прежним confidence_threshold, чтобы переведённая конфигурация вела себя как раньше, а ещё его можно написать вручную. Сертификат помечает такие метки как несертифицированные и сообщает их измеренную ошибку на калибровочных метках, а экспорт даёт им статус accepted_uncertified.

Результаты примеров исследований

Три примера исследований из examples/labeling-engine/ запускались с источниками LLM, судьи и визуальной модели, направленными на Gemma 12B на сервере vLLM, а не на модели Ollama, которые используют закоммиченные конфигурации. README каждого примера показывает изменённый блок источника, а с моделями Ollama по умолчанию числа будут другими.

  • Тональность текста (481 отзыв, Gemma 12B с уверенностью по logprob как последний уровень). selective_risk на 10% и 0.9 был выполнен на 165 человеческих метках, и было принято 316 машинных меток. Их измеренная ошибка относительно эталонных меток составила 7,6%, а интервалы PPI накрыли истинную распространённость всех трёх меток.
  • Изображения (128 изображений COCO, YOLOv8n и Gemma 12B как визуальная модель). Метка «есть человек» была сертифицирована на уровне 10%. Контракт по рамкам остался insufficient, потому что рамки YOLOv8n были неверны на 32% dev-изображений при IoU 0,5.
  • Трассы агентов (288 трасс, Gemma 12B как судья). Судья был прав на 80% трасс, но его уверенность всё время равнялась 1,0, поэтому selective_risk оказался невыполнимым. Тест альтернативного разметчика дал недостаточно данных: у машины 0,82 против 0,93 у людей. PPI оценил успешность задачи в 0,66 [0,57; 0,76], тогда как истинное значение было 0,60, а один судья давал 0,76, на 16 пунктов выше.

Дополнительные материалы

Подробности реализации смотрите в исходной документации.

Литература

  1. Angelopoulos, A. N., Bates, S., Candès, E. J., et al. (2025). Learn then test: Calibrating predictive algorithms to achieve risk control. The Annals of Applied Statistics. https://doi.org/10.1214/24-AOAS1998
  2. Angelopoulos, A. N., Bates, S., Fannjiang, C., et al. (2023). Prediction-powered inference. Science, 382(6671). https://doi.org/10.1126/science.adi6000
  3. Angelopoulos, A. N., Duchi, J. C., and Zrnic, T. (2023). PPI++: Efficient prediction-powered inference. arXiv. https://arxiv.org/abs/2311.01453
  4. Calderon, N., Reichart, R., and Dror, R. (2025). The alternative annotator test for LLM-as-a-judge: How to statistically justify replacing human annotators with LLMs. Proceedings of ACL 2025. https://aclanthology.org/2025.acl-long.782/