Обучение на практике: отрицательные результаты одного проекта
Модель, которая ни разу не видела вопроса без ответа, не умеет молчать. Это свойство данных, а не интеллекта.
Сцена: хеш от названия алгоритма
Пользователь пишет:
сделай хэш 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 потерянных аргументов), но это именно обмен, а не бесплатное улучшение.
Читателю полезнее знать цену гарантии, чем красивую цифру. Система, у которой главная метрика ноль, а отказ — в трёх четвертях случаев, честна и почти бесполезна; ровно поэтому цена отказов стоит в отчёте на той же странице, что и главная метрика.
Короткая практика
Вам показывают отчёт: «после дообучения доля неверных ответов упала почти до нуля». Какие четыре вопроса нужно задать до того, как обсуждать сам результат?
Возможный набор:
- Кто написал набор задач и видел ли он реализацию? Если да — измерено соответствие набора коду.
- Какова доля отказов и переотказов? Без этой пары «почти ноль» достигается молчанием.
- Прогонялись ли контроли — оракул, «всегда отказывать», «всегда отвечать»? Если нет, неизвестно, работает ли сама шкала.
- В какую сторону метрике запрещено ошибаться и как это проверялось? Ошибка в безопасную сторону выглядит как успех.
И пятый, если ответы на первые четыре устроили: что именно изменилось — данные или модель? Ответ «мы взяли модель побольше» и ответ «мы добавили класс отказов» означают разные будущие расходы.
Правило главы: измерение стоит ровно столько, сколько стоит его самое слабое допущение. Набор, написанный автором реализации; метрика, которую никто не проверял контролем; отказ, которому не учили, — каждое из этих допущений способно превратить хороший отчёт в описание того, чего нет.
Источники
Все числа этой главы взяты из отчётов одного проекта и подставлены в текст
автоматически, из отдельного файла данных: data/training-results.toml. У каждой
записи в нём есть значение, единица, путь к отчёту и дата. Числа, которые ещё не
измерены, помечены как незакрытые, и сборка главы с ними падает.
Опорные работы, на которых стоят приёмы этой главы, разобраны в соседних статьях трека:
- Дообучение: LoRA, QLoRA, инструкционный тюнинг — механика обучения, гигиена датасета, регресс общих способностей.
- Оценка и бенчмарки — собственные наборы, LLM-судья, размер выборки и доверительные интервалы.
- RAG и продвинутый RAG — откуда берётся мимо-цитата и чем лечится.
- Локальные модели — квантование и требования к железу.
- Оценка моделей — доверительные интервалы, калибровка и сравнение моделей в классическом ML.
Что дальше
Эта глава закрывает трек практикой: всё, что раньше описывалось как техника, здесь прошло через замер и частично не подтвердилось. Если хочется продолжить с той же стороны — со стороны данных и их качества — дальше лежит Инженерия данных; если со стороны эксплуатации моделей — MLOps.
Общая карта треков — в дорожной карте.