Пилот, который сам собирает себе разметку


Когда стоит задача научить ИИ ловить аномалии, мы первым делом смотрим, на чём эта модель будет учиться. Справочники и источники данных можно привести в порядок заранее — это делается до всякой модели, про такую подготовку был отдельный разбор. С разметкой фокус не проходит. Её нельзя подготовить впрок: никто никогда не отмечал задним числом, где была аномалия, а где норма, и этой истории не существует, пока кто-то не начнёт выносить вердикты на живом потоке. Собирать её отдельным этапом — это полгода, а то и год ждать, прежде чем система хоть что-то начнёт делать. Мы этот путь сокращаем: разворачиваем пилот, который производит разметку сам, пока уже работает и приносит пользу.
Меня зовут Алексей, я руковожу ML-направлением в компании. Приём сбора разметки пилотом у нас отработан на разных задачах; покажу его на контроле закупок — поиске завышенных цен, нецелевых и внетендерных закупок. Та самая задача, где эталон рыночной цены я в прошлый раз честно оставил открытым вопросом: на ней как раз и видно, что запускаться можно, не дожидаясь, пока этот эталон появится.
Почему тут нельзя просто взять модель, я уже разбирал отдельно: supervised обучать не на чем, unsupervised на редких событиях сыплет ложными, LLM без эталона уверенно называет завышенную цену рыночной. Итог один: сперва слой на правилах, модель — сверху и позже. Дальше — про то, как именно этот слой превращается в машину для сбора разметки.
Журнал как главный артефакт пилота
Центральный элемент такого пилота — не модель и не правила, а журнал. Правила отбирают подозрительные заявки, экономист выносит по каждой вердикт, а система в этот момент пишет событие. Важно, что именно писать: одно событие должно быть одновременно и строкой отчёта о предотвращённых потерях, и готовым обучающим примером. Поэтому в нём лежат:
● идентификатор заявки и позиции;
● какое правило сработало;
● значения признаков на момент срабатывания — сумма, остаток по позиции на складе, флаг превышения тендерного порога, канал заявки, наличие регистрационного номера;
● вердикт экономиста: подтвердить, вернуть, отклонить;
● причина — обязательна при отклонении;
● итог: оплачено, возвращено, переоформлено.
Пара «признаки плюс вердикт» — это ровно размеченный пример, на котором потом учится модель. Сумма по отклонённым с причинами — первая в истории компании прикидка предотвращённых потерь (оценка сверху: по дублям экономится весь счёт, по завышенной цене — только переплата). Один объект, две дыры закрыты. Rule-based слой здесь — генератор кандидатов и разметки: грубый фильтр поднимает поток на ревью, человек превращает каждое срабатывание в лейбл.
Стартовые правила не требуют эталона цены
Правило «цена выше рыночной» на старте мы не включаем — рыночной цены как данных ещё нет, её только предстоит собрать, и это отдельная задача (ту ветку, про ценовой коридор и источники, недавно подробно разобрали в комментариях к другому разбору, повторяться не буду). Но ждать готового эталона, чтобы запуститься, не нужно. Разметку отлично копят правила, которым эталон на старте не нужен вовсе:
● дубль закупки — позиция уже лежит на складе в достаточном остатке, сверяется с 1С;
● превышение тендерного порога — сумма выше границы, а тендерной процедуры не было;
● заявка без следа — устная или без регистрационного номера.
Ни одному из них не нужна «нормальная цена». Они формальные, срабатывают с первого дня, и каждое их срабатывание — вердикт экономиста — ложится в журнал лейблом. Ценовое правило подключается позже, когда ориентиры накоплены, и к этому моменту у нас уже есть и работающий контроль, и месяцы разметки. Так разрывается круг «курица или яйцо»: эталон нужен части правил, а разметка копится независимо от него.
Типовое спорное срабатывание выглядит буднично. Снабженец приносит счёт на топливный фильтр, а он уже лежит на складе — просто заведён в 1С под другим названием, и инициатор его не нашёл. Правило ловит это сверкой с остатками, экономист открывает заявку и возвращает её с короткой причиной: «есть на складе, брать не нужно». Эта строчка и есть лейбл — модель потом учится, что заявка на то, что уже лежит на остатках, аномалия, даже если название не совпадает буква в букву.
Одних флажков мало: в выборку идут и обычные заявки
Если в журнал попадает только то, что подняли правила, у модели вырастает слепое пятно. Она видит подозрительное, но никогда не видит заявку, мимо которой правило прошло молча, — и поэтому научится в лучшем случае точнее отсекать уже пойманное, а не находить новое. Поэтому к потоку флажков мы подмешиваем небольшую случайную выборку обычных, непомеченных заявок: экономист размечает и их тоже. Так в данных появляются примеры «правило молчало, а зря» — ровно то, на чём модель учится ловить то, что жёсткие пороги пропускают. Эта же случайная выборка — честная основа для замера: она не перекошена в сторону подозрительных, поэтому по ней видно и сколько модель ловит, и сколько дёргает зря.
За это приходится платить. Случайная выборка — это заявки, которые экономист смотрит сверх флажков, то есть часть той самой экономии времени, ради которой всё и затевалось. Долю держим небольшой и сознательно считаем её платой за то, чтобы модель вообще могла учиться на чём-то, кроме собственных же правил. Вторая сложность — дисбаланс: злоупотребления редки, и в случайной выборке их почти не будет, так что позитивных примеров копится мало, а значит обучение детектора на таком перекосе — отдельная история, которую придётся решать уже на стороне модели.
Качество разметки приходится контролировать отдельно
У человека в контуре есть обратная сторона: он не только даёт разметку, но и остаётся её главным слабым местом. Если экономист под нагрузкой начинает жать «подтверждаю» не глядя, журнал наполняется шумом, и модель потом честно выучивает этот шум — и выдаёт его уверенно. Поэтому инструментуем не только процесс, но и сами вердикты. Обязательная причина при отклонении, аудит-выборка решений, метрика доли «подтверждено без комментария» по каждому проверяющему. Как только эта доля ползёт вверх, сигнал загорается раньше, чем датасет испортится. Отдельно ловим не лень, а системную ошибку: экономист может быть стабильно неправ и при этом уверен, поэтому часть заявок уходит на второго проверяющего — сверяем, сходятся ли вердикты. Разметка — это тоже данные, и у них должно быть своё качество, иначе весь пилот работает вхолостую.
Когда включать модель — вопрос замера, а не календаря
Сколько вердиктов надо накопить, прежде чем ML начнёт обыгрывать голые правила, заранее не скажет никто. Поэтому мы не обещаем заказчику модель к дате. Вместо этого держим отложенную часть той самой случайной выборки и регулярно проверяем на ней: обгоняет ли модель, обученная на накопленном, голые правила — и по числу пойманных аномалий, и по доле ложных срабатываний. Пока не обгоняет — в бою работают правила, и это штатный режим, а не задержка. Модель выкатывается тогда, когда на этой выборке она измеримо лучше по обоим показателям, а не когда закончился спринт.
Что это даёт
Компания, которая никогда не считала свои потери на закупках, через несколько месяцев получает по ним первую цифру — не оценку пальцем в небо, а сумму по реальным отклонённым заявкам с причинами. И одновременно — размеченный датасет, которого на старте не существовало, собранный не отдельным дорогим проектом, а как побочный продукт ежедневной работы контроля.
Поэтому отсутствие разметки на входе мы не считаем поводом отложить запуск на полгода. Пилот проектируется как генератор собственных данных: он полезен с первого дня на простых формальных правилах и параллельно готовит почву для модели, которая встанет сверху, когда ей будет на чём стоять. Правильно спроектированный сбор — это когда к моменту, когда модель нужна, данные для неё уже собраны тем же пилотом.
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.