Разметка с моделью в цикле: предварительные метки и переобучение
Показывайте ответы обученной модели как предварительные метки, проверяйте сначала наименее уверенные из них и переобучайте модель по мере того, как разметчики исправляют ответы.
Новое в v2.14.0
В этом руководстве одно исследование тональности проходит цикл с моделью в цикле. Модель обучается на первых метках, её предсказания показываются разметчикам как предварительные метки, проверяющие исправляют наименее уверенные предварительные метки, а исправления попадают в следующий запуск. Руководство написано для владельцев исследований, которые уже обучили первую модель, как в руководстве Обучите свою первую модель, и теперь хотят, чтобы разметчики работали рядом с моделью. Каждый шаг, кроме одного, выполняется на ноутбуке с sklearn-text. Шаг отбора обучает небольшой трансформер и занимает на CPU несколько минут.
Проект с тональностью
Проект — учебный пример обучения моделей Potato, 160 синтетических отзывов на товары с одной схемой radio по имени sentiment. Предварительная метка — это одна метка, которую модель предложила для объекта до того, как объект разметил человек. Обратная запись — это шаг, который превращает предсказания запуска обучения в предварительные метки на странице разметки. К концу руководства проект обучает классификатор на первых 120 метках, публикует предварительные метки на оставшихся отзывах, выдаёт наименее уверенные отзывы первыми, переобучается по мере поступления новых меток и хранит отложенную выборку как версию датасета.
Предварительные метки ускоряют разметку и в то же время притягивают разметчиков к ответу модели. Когда этот компромисс оправдан, описывает материал Разметка с помощью модели, обученной на ваших метках, а о разметчиках, которые принимают предварительные метки не читая, — материал Как отличить проверку от механического принятия в данных с предварительными метками. Конфигурация ниже с самого начала исключает долю разметчиков из всех предварительных меток. Ответы исключённых разметчиков позволяют исследованию измерить это притяжение.
Настройка проекта
Установите Potato. sklearn-text не требует ничего, кроме базового пакета:
pip install --upgrade potato-annotationСкопируйте examples/ai-assisted/model-training/ из репозитория Potato в новую папку. В папке лежат data/reviews.json со 160 отзывами и data/gold.json с метками, по которым отвечает симулятор. Замените config.yaml примера этой конфигурацией:
annotation_task_name: "Review Sentiment (model in the loop)"
data_files:
- "data/reviews.json"
item_properties:
id_key: "id"
text_key: "text"
task_dir: "."
output_annotation_dir: "annotation_output"
require_password: false
admin_api_key: "demo-admin-key"
num_annotators_per_item: 1
assignment_strategy: llm_confidence
annotation_schemes:
- annotation_type: "radio"
name: "sentiment"
description: "Is this review positive or negative?"
labels:
- "positive"
- "negative"
sequential_key_binding: true
ai_support:
holdout:
fraction: 0.2
salt: "review-sentiment"
datasets:
enabled: true
model_training:
enabled: true
writeback:
schemas: ["sentiment"]
include_unlabeled: true
min_confidence: 0.6Каждый блок, добавленный к конфигурации примера, выполняет одну задачу.
assignment_strategy: llm_confidenceвыдаёт каждый объект, начиная с наименее уверенной предварительной метки. Объекты без предсказания идут последними, в порядке очереди.ai_support.holdoutскрывает любую ИИ-помощь, включая предварительные метки, у 20% пар разметчик–объект. Задайте исключение до начала разметки, потому что сменаfractionилиsaltпосреди исследования перемешивает группы.datasets.enabledпозволяет сохранить выборку запуска как версию датасета на последнем шаге.writeback.schemasназывает схемы, предсказания для которых видят разметчики. По умолчанию список пуст, и включение обучения ничего не меняет на странице разметки, пока в списке не названа схема.include_unlabeled: trueзаставляет каждый запуск предсказывать для отзывов, которые ещё никто не размечал. Без этой настройки запуск предсказывает только для отложенных объектов val и test этого запуска. У этих объектов человеческие метки уже есть.min_confidence: 0.6не показывает разметчикам предсказание с уверенностью ниже 0,6. Potato всё равно сохраняет предсказание вместе с запуском.
writeback.field сохраняет значение по умолчанию, predictions — поле объекта, в которое записываются предварительные метки. При значении по умолчанию блок pre_annotation не нужен. Проверьте конфигурацию перед запуском сервера:
potato validate config.yaml
potato start config.yaml -p 8000Разметьте начальный набор
Модели, обучаемой на ваших метках, сначала нужны метки. sklearn-text отказывается обучаться, если в выборке train меньше 10 объектов. Разметьте несколько отзывов на http://localhost:8000 или поручите трём симулированным разметчикам разметить по 40 отзывов по файлу с эталонными метками:
python -m potato.simulator --server http://localhost:8000 --users 3 \
--strategy gold_standard --competence good=1.0 --max-annotations 40 \
--gold-file data/gold.json --sequential --fast-mode --no-export--competence good=1.0 назначает каждому симулированному разметчику профиль good, который верно отвечает на 80–90% отзывов. При num_annotators_per_item: 1 три разметчика покрывают 120 разных отзывов и оставляют 40 неразмеченными для модели.
Обучите модель и прочитайте отчёт об обратной записи
Откройте http://localhost:8000/admin/training с админским ключом, выберите sklearn-text и схему sentiment и нажмите Train now. Тот же запуск начинается из командной строки:
curl -X POST -H "X-API-Key: demo-admin-key" -H "Content-Type: application/json" \
http://localhost:8000/admin/training/api/runs \
-d '{"trainer": "sklearn-text", "schemas": ["sentiment"]}'Запуск занимает несколько секунд. Potato делит размеченные отзывы на train, val и test (по умолчанию 70%, 15% и 15%), обучает классификатор на train и оценивает его на val и test. Затем маршрут status сообщает оценки и отчёт об обратной записи:
curl -H "X-API-Key: demo-admin-key" http://localhost:8000/admin/training/api/statusОдин запуск этого руководства вернул следующее, сокращённо до существенных здесь полей:
{
"metrics": {
"heldout_metric": "val macro_f1",
"heldout_score": 0.7333,
"n_train": 76.0,
"val_n": 20.0,
"test_n": 24.0,
"test_macro_f1": 0.8222
},
"writeback": {
"n_read": 84,
"n_written": 78,
"n_skipped_low_confidence": 6,
"n_skipped_train_split": 0,
"n_skipped_rejected": 0,
"n_skipped_schema_not_enabled": 0,
"summary": "Published 78 prediction(s) as prelabels."
}
}Запуск предсказал для 84 отзывов: 44 отзывов val и test и 40, которые никто не размечал. Шесть предсказаний оказались ниже порога 0,6, а остальные 78 стали предварительными метками. Ваши оценки и числа будут отличаться от запуска к запуску, потому что симулированные разметчики допускают случайные ошибки и размечают разные отзывы. Каждый счётчик пропусков называет одну из четырёх проверок обратной записи, которые Конфигурация перечисляет по порядку.
Обратная запись никогда не меняет человеческую метку. Предсказания попадают в данные объекта, а аннотации остаются в собственном состоянии каждого разметчика. Войдите под новым именем пользователя, чтобы увидеть результат. Следующий отзыв приходит с выбранным ответом модели:
Новый разметчик видит выбранный ответ модели и подтверждает или меняет его
Предварительная метка не сохраняется, пока разметчик только смотрит на неё. Предварительная метка становится ответом разметчика, когда он правит её или переходит к следующему отзыву. Поэтому отзыв без ответа не считается выполненным, а ответ модели не возвращается в обучение как человеческая метка.
Прочитайте оценку на отложенных объектах, прежде чем показывать предварительные метки разметчикам. Оценка val 0,73 macro-F1 на 20 отзывах оставляет много места для неверных предварительных меток. В справочнике по обратной записи в основном репозитории перечислены остальные счётчики пропусков и то, как каждый тип аннотации показывает предварительную метку.
Проверьте наименее уверенные предварительные метки
При llm_confidence следующий разметчик получает 40 неразмеченных отзывов, упорядоченных по уверенности опубликованной предварительной метки, начиная с самой низкой. Вторая стратегия выдаёт предварительные метки для контроля качества, а не для разметки. assignment_strategy: model_review выдаёт только объекты с предварительной меткой, начиная с наименее уверенной, и пропускает объекты без предсказания. Переключайтесь на model_review для прохода проверки вывода модели и оставайтесь на llm_confidence, пока каждому объекту ещё нужна метка. Справочник по проверке модели в основном репозитории объясняет обе очереди.
Отчёт о проверке модели показывает очередь и то, что модель оставила без предсказания:
curl -H "X-API-Key: demo-admin-key" http://localhost:8000/admin/model-reviewОтчёт по запуску выше, со сокращёнными списками:
{
"n_items": 160,
"n_prelabelled": 78,
"queue": ["review_062", "review_024", "review_044", "review_122", "..."],
"worst_first": ["review_062", "review_024", "review_044", "..."],
"n_empty": 82,
"empty_prediction_ids": ["review_006", "review_007", "review_008", "..."],
"provenance": {
"review_062": {
"sentiment": {
"run_id": "20261104T043431-de5686",
"trainer": "sklearn-text",
"heldout_metric": "val macro_f1",
"heldout_score": 0.7333
}
}
}
}Место объекта в queue определяется его самой низкой уверенностью предсказания среди схем. worst_first — наименее уверенные 20% очереди, но не меньше одного объекта. empty_prediction_ids перечисляет объекты без предварительной метки. В этом проекте 82 пустых объекта — это 76 обучающих отзывов и 6 предсказаний ниже порога. Обучающие отзывы предварительной метки не получают никогда.
provenance записывает, откуда взялась каждая предварительная метка. Для каждого объекта в очереди, предварительную метку которого записал запуск обучения, запись называет запуск, тренер и оценку запуска на отложенных объектах. У предварительной метки, пришедшей вместе с файлом данных, записи нет. Когда предварительная метка выглядит неверной, идентификатор запуска ведёт на страницу запуска /admin/training/runs/<run_id>, где видны параметры и выборка каждого объекта.
Запись вердиктов
Вердикт фиксирует суждение проверяющего об одной предварительной метке отдельно от аннотации проверяющего. "Модель оказалась права" и "человек независимо выбрал эту метку" — разные факты, и разделение этих фактов сохраняет возможность измерить точность модели. Potato принимает вердикты через один маршрут, от разметчика, которому назначен объект, или от администратора с ключом API:
curl -X POST -H "Content-Type: application/json" -H "X-API-Key: demo-admin-key" \
-d '{"instance_id": "review_062", "verdict": "correct", "schema_name": "sentiment"}' \
http://localhost:8000/api/model-review/verdictaccept означает, что модель оказалась права, correct означает, что человек изменил ответ, а reject означает, что модель ошиблась. reject сразу снимает предварительную метку с объекта, и последующие запуски больше не публикуют предсказание для этого объекта и этой схемы. Вердикт reject по-прежнему засчитывается против точности модели как отклонённая предварительная метка, а не как метка, которую модель пропустила, и повторная отправка того же вердикта не меняет ни один счётчик. Вердикт correct переносит объект в выборку train каждого последующего запуска, как описывает раздел об исправлениях ниже. Повторный вердикт для того же объекта заменяет первый, а неизвестный вердикт отклоняется с HTTP 400.
Упорядочите очередь по оценке отбора
Порядок по уверенности выдаёт первыми объекты, где верхний ответ модели слабее всего. Функция отбора в активном обучении ранжирует неразмеченные объекты по тому, насколько метка каждого из них помогла бы следующей модели. hf-text-classification и hf-image-classification принимают параметр acquisition, который записывает такую оценку в каждое предсказание, и llm_confidence затем выдаёт первой наивысшую оценку. Тренеру нужно дополнение для обучения на тексте:
pip install "potato-annotation[train-text]"Начните запуск с параметром в поле Parameters на странице Training или через API:
curl -X POST -H "X-API-Key: demo-admin-key" -H "Content-Type: application/json" \
http://localhost:8000/admin/training/api/runs \
-d '{"trainer": "hf-text-classification", "schemas": ["sentiment"],
"params": {"acquisition": "margin"}}'Параметр принимает пять значений:
acquisition | Оценка, записываемая в каждое предсказание |
|---|---|
least_confident | По умолчанию. Оценка не записывается, и объекты сохраняют порядок по уверенности. |
margin | 1 минус разрыв между двумя верхними вероятностями. |
entropy | Энтропия предсказанного распределения, приведённая к диапазону 0–1. |
bald | Взаимная информация между меткой и весами модели, по mc_samples (по умолчанию 10) прямым проходам с включённым dropout. Метод описан в Houlsby et al. (2011). Оценка высока там, где модели не хватает данных, и низка там, где объект неоднозначен при любых данных. |
coreset | Расстояние каждого объекта до ближайшего обучающего или уже выбранного объекта по эмбеддингам модели. Метод описан в Sener and Savarese (2018). Оценка распределяет очередь по данным, поэтому десять почти одинаковых копий одного трудного объекта не занимают первые десять мест. |
Предсказание из запуска с margin несёт оценку рядом с уверенностью:
{"label": "positive", "confidence": 0.5207,
"probabilities": {"negative": 0.4793, "positive": 0.5207},
"acquisition": 0.9586}При llm_confidence первыми идут объекты с оценкой отбора, затем объекты только с уверенностью, затем объекты, у предсказания которых нет уверенности, затем объекты без предсказания. acquisition работает на схемах с одной меткой (radio, select, likert). Порядок читает опубликованные предварительные метки, поэтому схема должна быть в writeback.schemas, а чтобы неразмеченные объекты вообще получили оценку, должно быть задано include_unlabeled: true. model_review игнорирует оценку и сохраняет порядок по уверенности, потому что проход проверки ищет неверные ответы модели. Подробности есть в разделе основного репозитория о выборе следующего вопроса.
Сравните оценку нового запуска на отложенных объектах с оценкой запуска sklearn-text, прежде чем оставлять предварительные метки нового запуска. Potato хранит одно предсказание на объект и схему, и предсказание более нового запуска заменяет более старое. Если предсказание нового запуска сохранено, но не показано, например из-за min_confidence, предварительная метка более старого запуска тоже снимается с объекта, как описывает Конфигурация. В одном запуске этого руководства на CPU три эпохи модели по умолчанию (epochs: 3 в params) дали 0,52 macro-F1 на test при 84 обучающих отзывах, ниже запуска sklearn-text на тех же метках, и запуск опубликовал 37 предварительных меток. Чтобы отозвать предварительные метки запуска, удалите предсказания запуска:
curl -X DELETE -H "X-API-Key: demo-admin-key" \
http://localhost:8000/admin/training/api/runs/<run_id>/predictionsОтзыв удаляет сохранённые предсказания запуска и снимает предварительные метки запуска с объектов. Предварительные метки более раннего запуска не возвращаются, поэтому обучите заново, чтобы опубликовать их снова.
Переобучайте автоматически
auto_retrain сам начинает запуск по мере поступления аннотаций. Добавьте блок в model_training, когда оценка запуска на отложенных объектах оправдывает показ предварительных меток:
model_training:
enabled: true
writeback:
schemas: ["sentiment"]
include_unlabeled: true
min_confidence: 0.6
auto_retrain:
enabled: true
trainer: sklearn-text
schemas: ["sentiment"]
update_frequency: 25
min_interval_s: 600Potato проверяет условие после каждого сохранения аннотации. Запуск начинается, когда с последнего автоматического запуска поступило 25 аннотаций и с завершения последнего запуска прошло 600 секунд. Оба значения заданы по умолчанию, а trainer и schemas обязательны. params передаёт параметры тренера, как поле Parameters на странице Training.
Включайте auto_retrain после первого ручного запуска, а не с первой аннотации. Проект без единого запуска обучается, как только в проекте набирается update_frequency аннотаций. При update_frequency: 20, заданном с самого начала, первый автоматический запуск этого проекта обучился на 12 отзывах, получил macro-F1 1,0 на val по 3 отзывам и опубликовал 72 предварительные метки. Оценка на отложенных объектах по 3 отзывам почти ничего не говорит о следующих 72.
Когда проверка решает не начинать запуск, skip_reason в /admin/training/api/status называет причину, например "waiting for 3 more annotation(s)" или "waiting 299 more second(s) since the last run". Автоматические запуски не встают в очередь запусков. Следующее сохранение аннотации после того, как очередь опустеет, запускает новую проверку. Счётчики хранятся в памяти, и после перезапуска проект, который уже запускался, считает новые аннотации с момента перезапуска. Тренеру, лицензию которого нужно подтвердить (ultralytics-detect, ultralytics-segment, layoutlmv3), в блоке нужен также accept_licence: true, иначе каждый автоматический запуск пропускается. Полная конфигурация с обоими блоками проходит potato validate config.yaml --strict.
Исправленные и закреплённые объекты в обучении
Два вида объектов попадают в выборку train каждого последующего запуска, где бы объект ни стоял в стабильном разбиении. Первый вид — объект с вердиктом correct. Исправления приходят от объектов, на которых модель ошибалась сильнее всего, а в val или test эти исправления оценивали бы модель на выборке её собственных ошибок. Вердикты accept и reject объект не переносят. Второй вид — закреплённый объект, который администратор отправляет в обучение вручную:
curl -X POST -H "X-API-Key: demo-admin-key" -H "Content-Type: application/json" \
http://localhost:8000/admin/training/api/pinned \
-d '{"instance_ids": ["review_007", "review_008"]}'Закреплённый объект обучается, как только кто-то его разметил, а до тех пор каждый запуск считает его ожидающим. При curation: {enabled: true} кнопка Train on these рядом со срезом на странице Catalog закрепляет всё, что соответствует срезу, например встроенный срез model-found-nothing. DELETE /admin/training/api/pinned снимает закрепление.
Запуск, который перенёс отложенные объекты в train, пишет в журнал предупреждение с числом объектов. После одного вердикта correct следующий запуск в этом руководстве записал следующее:
1 held-out item(s) moved to train (corrected: 1). Held-out scores are computed without them, so they are not directly comparable with a run that had them.Числа лежат в manifest.json пакета запуска, в stats.forced_train. Сравнивайте оценки на отложенных объектах между запусками с учётом этого предупреждения, потому что два запуска оценивали разные объекты.
Сохраните выборку test запуска как датасет
Оценка на отложенных объектах меняется вместе с выборками, а фиксированный набор объектов — то, что делает две модели сопоставимыми. Раздел Keep this split as a dataset на странице запуска сохраняет объекты val, test или train запуска вместе с согласованными человеческими метками этих объектов как новую версию датасета. Выберите выборку, введите имя датасета и нажмите Save as dataset version. Маршрут за кнопкой принимает тот же запрос из командной строки:
curl -X POST -H "X-API-Key: demo-admin-key" -H "Content-Type: application/json" \
http://localhost:8000/admin/training/api/runs/<run_id>/pin-split \
-d '{"dataset": "sentiment-test", "split": "test"}'Ответ называет новую версию и число объектов, здесь 23 отзыва test, сохранённых как версия v0001. После сохранения версия не меняется, поэтому последующие модели и промпты LLM в запуске экспериментов датасетов можно оценивать на тех же самых объектах. Сохранение в датасет, у которого уже есть версии, начинает новую версию только с этой выборкой, поэтому объекты val более позднего запуска не смешиваются с объектами, на которых этот запуск обучался. Входные данные каждого примера не содержат поля предварительной метки, поэтому оценка не может прочитать ответ модели. Маршрут возвращает HTTP 400, если не задано datasets.enabled.
Исключение разметчиков из предварительных меток
Блок ai_support.holdout в конфигурации с первой аннотации оставляет 20% пар разметчик–объект без помощи. Для исключённой пары страница разметки не показывает предварительную метку, а данные объекта, отправляемые в браузер, не содержат ни writeback.field (здесь predictions), ни pre_annotation.field. Жеребьёвка — это хеш с солью для каждого разметчика и объекта, поэтому пара остаётся в одной группе при каждом посещении и после перезапуска. Если у отзыва несколько разметчиков, у большинства отзывов есть и разметчики с помощью, и исключённые разметчики. Вкладка AI influence админ-панели сравнивает две группы по тому, как часто ответы совпадают с моделью, как часто ответы совпадают с эталоном и какова согласованность внутри каждой группы. При num_annotators_per_item: 1 каждый отзыв попадает только в одну группу, поэтому для исследования, измеряющего эффект, задайте значение больше 1. Как читать сравнение, объясняет материал Разметка с помощью модели, обученной на ваших метках.
Дополнительные материалы
- Обучение моделей объясняет цикл обучения и страницу Training.
- Конфигурация обучения моделей перечисляет все ключи
writebackиauto_retrainсо значениями по умолчанию. - Обучите свою первую модель настраивает этот проект и первый запуск
sklearn-text. - Обучение моделей детекции и сегментации на разметке изображений проходит тот же цикл с детектором рамок на изображениях.
- Разметка с помощью модели, обученной на ваших метках описывает, когда предварительные метки экономят время, а когда закрепляют ответы.
- Как отличить проверку от механического принятия в данных с предварительными метками находит разметчиков, принимающих предварительные метки не читая.
- Активное обучение описывает выбор следующего объекта без показа ответа модели.
- Справочник по обучению моделей в основном репозитории приводит каждую проверку и каждый маршрут обратной записи.
Источники
- Houlsby, N., Huszár, F., Ghahramani, Z., and Lengyel, M. (2011). Bayesian Active Learning for Classification and Preference Learning. arXiv:1112.5745. https://arxiv.org/abs/1112.5745
- Sener, O., and Savarese, S. (2018). Active Learning for Convolutional Neural Networks: A Core-Set Approach. ICLR 2018. https://arxiv.org/abs/1708.00489