Контракты движка разметки
Шесть видов контрактов, которые движок разметки проверяет по человеческим меткам, от селективного риска и интервалов 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 задаёт наибольшую допустимую долю ошибок среди принятых меток и обязателен:
- 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_annotation | 1 - средний временной IoU | 0.5 |
3D-объекты (spatial_annotation) | 1 - средний 3D IoU | 0.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 сообщает интервал для распространённости каждой метки или для среднего балла с поправкой на ошибки машины:
- 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 widePrediction-powered inference (Angelopoulos et al., 2023) корректирует оценку машины по её ошибке на выборке, размеченной людьми. Интервал корректен при любом качестве машины. Хорошая машина делает его уже, чем могли бы одни люди, а плохая не делает шире. При равных вероятностях включения движок использует PPI++ с подобранным весом, а при неравных (стратифицированная выборка или выборка по неопределённости) разностную оценку Хорвица-Томпсона. Используется та машинная метка, которую выдал бы каскад. Поле by копируется из объектов при их регистрации. Без max_width контракт выполнен, как только у него достаточно меток для отчёта.
no_worse_than_human
no_worse_than_human спрашивает, согласуется ли машина с людьми не реже, чем человек:
- 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 прогнозирует, что даст бюджет человеческих меток, до того как вы его потратите:
- 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 сравнивает распределение меток машины с тем, как расходятся люди, в субъективных задачах, где люди расходятся по причинам, которые должны остаться в данных:
- 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 принимает машинные метки выше фиксированной уверенности и не проводит статистического теста:
- id: old-rule
kind: threshold_legacy
schema: sentiment
threshold: 0.8potato 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 пунктов выше.
Дополнительные материалы
- Движок разметки
- Конфигурация движка разметки, включая ключи
humans, которые читают эти контракты - Согласованность разметчиков и согласованность по геометрии, где используются те же потери для областей, сегментов и 3D-объектов
Подробности реализации смотрите в исходной документации.
Литература
- 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
- Angelopoulos, A. N., Bates, S., Fannjiang, C., et al. (2023). Prediction-powered inference. Science, 382(6671). https://doi.org/10.1126/science.adi6000
- Angelopoulos, A. N., Duchi, J. C., and Zrnic, T. (2023). PPI++: Efficient prediction-powered inference. arXiv. https://arxiv.org/abs/2311.01453
- 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/