ИИ-агенты и prompt engineering Обучение на практике: отрицательные результаты одного проекта
0%

Обучение на практике: отрицательные результаты одного проекта

Обучение на практике: отрицательные результаты одного проекта

Модель, которая ни разу не видела вопроса без ответа, не умеет молчать. Это свойство данных, а не интеллекта.

Сцена: хеш от названия алгоритма

Пользователь пишет:

сделай хэш SHA-256, пожалуйста

Правильное поведение — переспросить: не сказано, что именно хешировать. Дообученная модель вместо этого вызвала утилиту хеширования и подставила в поле «текст» строку SHA-256. Ответ вернулся мгновенно, он был корректно вычислен и формально верифицирован: утилита действительно посчитала SHA-256 от строки «SHA-256».

Ошибка здесь не в вычислении и не в формате. Она в том, что модель обязана была отказаться, а весь её обучающий опыт говорил, что у запроса всегда есть ответ.

Эта глава — разбор одного проекта, где такие вещи измерялись, а не предполагались. Механика дообучения и устройство оценки разобраны раньше; здесь речь о том, что происходит, когда всё это применяют к живой задаче и половина результатов оказывается отрицательной.

Задача проекта: локальный ассистент, который отвечает только из проверяемых источников — вывода детерминированной утилиты, дословной цитаты из корпуса или формального сертификата. Маленькая модель в нём работает маршрутизатором: она выбирает инструмент и вытаскивает аргументы, но не порождает содержание. Главная метрика одна — доля случаев, когда система выдала содержательный ответ, помеченный как проверенный, а он неверен или ничем не подтверждён.

Правило 1. Отказу учат отдельным классом данных

В обычном обучающем наборе у каждого вопроса есть ответ. Модель извлекает из этого мета-правило «ответ существует всегда» — и на запросе, где ответа нет, уверенно сочиняет. Никакой промпт этого не лечит: правило впечатано в веса.

Лечится долей примеров, где правильный ответ — отказ. В нашем датасете она составила 24,0 % при целевом коридоре 15–25 %.

Результат по осознанному отказу на red-team-части замера: 75,3 % → 91,3 %. Главная метрика при этом 17,2 % → 9,2 %. По классу «ложная посылка в формулировке» неверных ответов было 13/30, стало 1/30; по классу «аргумент не назван» — 16/40 и 10/40.

Долю отказов пришлось удерживать: первый прогон генератора дал 31,1 %, то есть вышел за верхнюю границу коридора, и старые классы урезали. Верхняя граница здесь не эстетика — она ограничивает переотказ, о цене которого ниже.

Важнее самого прироста — три детали конструкции.

Отказ бывает разных сортов, и учить надо каждый. «Я не понял запрос», «такого инструмента нет», «данные не названы» и «посылка вопроса ложна» — это четыре разных поведения. Класс, обучающий одному, не переносится на остальные.

Отсутствия значения недостаточно. В первой версии датасета все примеры «аргумент не назван» опускали значение целиком. Модель выучила правило «нет ни одного кандидата → отказ» вместо нужного «нет подходящего кандидата → отказ». Ровно поэтому она и захешировала строку SHA-256: кандидат в тексте был. Пришлось добавить 2 192 примеров, где в запросе есть посторонний токен, синтаксически годящийся в аргумент.

Отказ должен опираться на ложь, а не на тему. «Посчитай 20 процентов от 8000» — законный запрос. «Раз ставка по умолчанию 20 %, посчитай по этой ставке скидку на 12 000» — нет, потому что ставка взята из выдумки. Если эту границу не провести в данных, класс научит отказываться от всей темы.

Правило 2. Данные важнее базовой модели

Мы измерили оба рычага по отдельности, на одном и том же замере.

Смена датасета при неизменной базе: осознанный отказ 83,3 % → 91,3 % на большой модели и 75,3 % → 90,7 % на маленькой.

Смена базы (втрое больше параметров) при неизменном датасете: осознанный отказ вырос на 0,6 п.п., точность маршрутизации — с 81,0 % до 85,0 %.

Втрое больший размер купил доли процента там, где датасет покупал десятки. При этом обучение подорожало примерно вдвое по времени (38,2 мин против 76,9 мин) и вдвое по памяти (3,2 ГБ против 6,6 ГБ).

Практический вывод не «берите модель поменьше», а «сначала измерьте, что вы покупаете размером». Размер решает там, где не хватает общих способностей; поведение — отказ, формат, дисциплина вызова инструментов — покупается данными и почти не зависит от числа параметров.

Есть и обратная сторона, которую стоит назвать: необученная большая модель умеет отказываться заметно лучше необученной маленькой. У маленькой базы осознанный отказ был 9,3 %, у большой — 70,7 % без всякого дообучения. Дообучение подтягивает обе к одному потолку, поэтому в замере «до/после» эффект дообучения на маленькой модели выглядит больше, чем он есть.

Правило 3. Русскоязычный файнтюн — не русская модель

Ожидание «модель дообучили на русском, значит, русский текст будет экономнее по токенам» звучит естественно и оказалось неверным.

Мы посчитали fertility — сколько токенов приходится на слово — на срезе живой русской прозы объёмом 3 000 абзацев. Результат: у русскоязычных SFT-моделей fertility совпал с исходной моделью до четвёртого знака — 2,344 ток./слово против 2,344 ток./слово. Не «немного лучше», а ровно то же самое. Причина проста: русскоязычный SFT — это дообучение поверх чужой базы с неизменённым словарём. Токенизатор никто не трогал.

Экономию дала единственная модель с перестроенным словарём: 1,686 ток./слово токена на слово, то есть −28,1 %. На коротких пользовательских запросах выигрыш меньше — −15,0 %: там латиница, числа и идентификаторы, где экономить нечего.

Обратите внимание на размер словаря: у экономной модели он 145 152 против 151 669 у остальных. Экономию даёт не объём словаря, а его состав — то, какие подслова в нём есть.

Экономия токенов — это не абстракция. Она прямо определяет, сколько описаний инструментов помещается в контекст: у нас все схемы занимали 5 394 токенов вместо 6 486 токенов, то есть при том же бюджете помещается примерно на пятую часть больше. За одну эпоху по тому же датасету прошло 14,85 млн токенов вместо 18,21 млн, и обучение заняло 56,4 мин вместо 71,8 мин.

И вторая половина результата, ещё менее ожидаемая: русский SFT проиграл собственной базе по качеству. На честной паре — та же базовая модель, тот же токенизатор, тот же размер, различается только русское дообучение поверх — главная метрика составила 15,6 % против 12,8 % у неруссифицированной базы, а по ложным посылкам 11/30 против 6/30.

Объяснение не мистическое. Инструкционный тюнинг на русских диалогах улучшает свободную беседу — ровно то, чего в нашей задаче нет: модель не пишет ответ, она классифицирует запрос и извлекает аргументы. Чужое дообучение улучшило неиспользуемое и размыло используемое.

Переносимое правило: свойство модели проверяется замером этого свойства, а не её названием. Fertility считается за минуты и не требует ни одной обученной модели — это самый дешёвый честный факт о базе, который можно получить.

Правило 4. Контроли прогоняют раньше метрик

Прежде чем измерять систему, надо доказать, что шкала работает. Для этого нужны не метрики, а вырожденные системы с заранее известным результатом:

  • оракул — отвечает из эталонов. Обязан дать максимум по всем метрикам;
  • всегда отказывать — обязан дать ноль ошибок и стопроцентный переотказ;
  • всегда отвечать — обязан дать верхнюю границу ошибок.

Оракул дал 0,0 % ложных ответов и 100,0 % точности выбора инструмента; «всегда отказывать» — 0,0 % ошибок при 100,0 % переотказа; «всегда отвечать» — 100,0 % ошибок и 250 случаев необоснованной пометки «проверено».

Первое, что даёт эта тройка, — интерпретируемые границы: становится видно, что система, отказывающая всегда, получает идеальную главную метрику. Значит, рядом с главной метрикой обязана стоять цена отказов, иначе оптимизация сойдётся к молчанию.

Второе — важнее. Оракул ловит ошибки в самих метриках. Он поймал одну: сравнение аргументов шло по сериализованному JSON, а не по значениям, и идеальная система получала 11 ложных ошибок из ста задач маршрутизации. Ни один прогон реальной системы этого бы не показал — ошибку просто списали бы на модель.

Правило простое: оракул прогоняется заново после каждой правки кода метрик. Если идеальная система не получает максимум, сломана не система, а измерение.

Правило 5. Метрика может врать в безопасную сторону

Ошибка в метрике не обязана выглядеть как ошибка. Наш парсер ответов читал поле «ответ» буквально. Модель же регулярно оставляла это поле пустым, а содержание писала свободным текстом ниже — где оно попадало в поле «причина отказа». Пустое поле «ответ» классифицировалось как честный отказ.

То есть содержательный и неверный ответ засчитывался системе в плюс. После исправления доля ложных ответов на отвеченной выборке выросла вдвое.

Это тот тип ошибки, который не находят по расхождению с ожиданиями: цифры выглядели хорошо, и именно поэтому их никто не проверял. Помогает одно дисциплинарное правило: знать заранее, в какую сторону вашей метрике запрещено ошибаться, и проверять именно эту сторону. Для системы, которая обещает не выдумывать, занижение доли выдумок — единственное недопустимое направление; завышение неприятно, но безопасно.

Практический приём: держите рядом с метрикой «улику» — отдельный счётчик, не зависящий от классификатора. У нас это доля ответов, где нашлась строка из списка заведомо неверных. Она считается прямым поиском подстроки и не может сломаться вместе с парсером.

Второй случай той же болезни нашёлся в отчёте об обучении и решается тем же приёмом. «Отказ» засчитывался, если ответ не содержал разбираемого содержания, — а необученная маленькая модель просто ломалась. У неё засчитанный отказ составил 75,3 %, а осознанный — 9,3 %: харнесс принял 115 нечитаемых ответов из 250 за честное «не знаю». У необученной большой модели разрыв меньше (81,3 % против 70,7 % при 21 нечитаемом ответе), у финальной модели колонки совпадают при 1 нечитаемом ответе.

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

Правило 6. Замер на не-held-out наборе бесполезен

Это центральный урок главы, и он стоил больше всего.

Система показывала 1,8 % ложных ответов на основном наборе задач и ровно 0,0 % на red-team-части — то есть «не выдумывает вообще». Набор был написан аккуратно: каждая цитата сверена побайтово, каждое утверждение об отсутствии темы в корпусе подтверждено настоящей командой поиска.

Затем такой же по правилам набор из 230 задач написал автор, которому запретили открывать реализацию — ни целиком, ни поиском по содержимому. Ему были доступны только исходные корпуса, схема задачи и правила валидации.

Главная метрика на этом наборе: 13,9 % против 1,8 %, то есть ×7,7. На red-team-части — 13,3 % вместо 0,0 %. Достоверность цитат упала с 45,0 % до 20,4 %.

Ни одна группа задач не подбиралась «под известные дыры» — реализацию просто не открывали. Прежний ноль был свойством набора, а не системы.

Механизм провала оказался неожиданным и поучительным. В 28 из 32 пробоев система не выдумала ни одного символа: она извлекла настоящую дословную цитату из корпуса — и та отвечала на другой вопрос. На вопрос «как записать лемму» приходил фрагмент из статьи по теории графов. С точки зрения проверки «есть ли этот текст в источнике» ответ безупречен. С точки зрения пользователя это ложь.

Отсюда важное различие, которое стоит держать в голове при проектировании любой проверки: выдумать можно не только факт, но и релевантность. Гейт, который спрашивает «есть ли этот текст в корпусе», такой ответ пропускает по построению. Нужен другой вопрос: «отвечает ли этот фрагмент на заданный?»

Тот же урок независимо повторился на другом слое системы. Порог отсечения для поиска был откалиброван на наборе, где «чужие» вопросы были про кулинарию и спорт: получалось 0,975 полноты при нуле ложных принятий. На наборе, где чужие вопросы — правдоподобные вопросы про соседние технические темы, то же условие держится только при полноте 0,327. Ничего в системе не менялось; изменилась трудность отрицательных примеров.

Что из этого следует практически. Набор, написанный человеком, который видел код, измеряет соответствие набора коду. Чтобы измерять систему, автор набора должен быть отделён от реализации — и это дешевле, чем кажется: нужен не другой отдел, а другой человек и запрет на чтение конкретных каталогов, зафиксированный письменно. Второй способ, если людей мало, — писать набор до реализации.

Правило 7. Квантование проверяют отдельно по каждому поведению

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

Мы прогнали полный замер на каждом уровне квантования, а не только замер точности. Тонкое поведение выдержало лучше, чем ожидалось: осознанный отказ 91,3 % в исходной точности, 92,0 % при восьми битах и 89,3 % при четырёх — при сжатии файла с 3 447 МБ до 1 107 МБ.

Но у маленькой модели на четырёх битах сдвинулось другое: переотказ вырос с 13,0 % до 16,0 %, а точность маршрутизации упала с 81,0 % до 78,0 %. То есть модель стала осторожнее и хуже попадала в инструмент — при почти неизменной главной метрике.

Вывод не «квантование безопасно» и не «квантование опасно», а процедурный: после квантования прогоняется тот же полный набор, что и до, включая red-team. Замер одной агрегированной точности сдвиг такого рода не покажет.

Цена гарантии

Честная картина обязана включать то, что обычно опускают.

Итоговая система отказывается отвечать в 75,6 % случаев на независимом наборе и в 66,5 % на основном. Из отказов на задачах, где ответ был доступен, — 46,4 %. Это не побочный эффект, а прямая цена архитектуры, которая обещает не выдумывать: каждый отказ — это вопрос, на который источник ответа существовал, а система его не нашла или не смогла подтвердить.

Есть и цена внутри самого дообучения. Класс, который учит не доверять постороннему токену, переносит часть осторожности на настоящие второстепенные аргументы: точность извлечения аргументов упала с 93,7 % до 90,1 %, а переотказ на основном наборе вырос с 9,0 % до 13,0 %. По счёту задач обмен выгоден (20 исправленных против 2 сломанных отказов и 2 потерянных аргументов), но это именно обмен, а не бесплатное улучшение.

Читателю полезнее знать цену гарантии, чем красивую цифру. Система, у которой главная метрика ноль, а отказ — в трёх четвертях случаев, честна и почти бесполезна; ровно поэтому цена отказов стоит в отчёте на той же странице, что и главная метрика.

Короткая практика

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

Возможный набор:

  1. Кто написал набор задач и видел ли он реализацию? Если да — измерено соответствие набора коду.
  2. Какова доля отказов и переотказов? Без этой пары «почти ноль» достигается молчанием.
  3. Прогонялись ли контроли — оракул, «всегда отказывать», «всегда отвечать»? Если нет, неизвестно, работает ли сама шкала.
  4. В какую сторону метрике запрещено ошибаться и как это проверялось? Ошибка в безопасную сторону выглядит как успех.

И пятый, если ответы на первые четыре устроили: что именно изменилось — данные или модель? Ответ «мы взяли модель побольше» и ответ «мы добавили класс отказов» означают разные будущие расходы.

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

Источники

Все числа этой главы взяты из отчётов одного проекта и подставлены в текст автоматически, из отдельного файла данных: data/training-results.toml. У каждой записи в нём есть значение, единица, путь к отчёту и дата. Числа, которые ещё не измерены, помечены как незакрытые, и сборка главы с ними падает.

Опорные работы, на которых стоят приёмы этой главы, разобраны в соседних статьях трека:

Что дальше

Эта глава закрывает трек практикой: всё, что раньше описывалось как техника, здесь прошло через замер и частично не подтвердилось. Если хочется продолжить с той же стороны — со стороны данных и их качества — дальше лежит Инженерия данных; если со стороны эксплуатации моделей — MLOps.

Общая карта треков — в дорожной карте.

Нашли неточность? Выделите фрагмент текста — рядом появится жучок.

Нужен разбор именно вашей ситуации?

Статья описывает общий случай. Если у вас частный — можно разобрать его отдельно, платно. А если не хватает целого материала, предложите тему: её оплачивают вскладчину, и она выходит открытой для всех.

Доска запросов