Перейти к содержимому
05 дек. 2024 г.·6 мин чтения

Хранение LoRA-адаптеров: имена, откат и архивирование

Хранение LoRA-адаптеров требует простых правил: как называть версии, проверять совместимость с базовой моделью, откатывать и убирать в архив.

Хранение LoRA-адаптеров: имена, откат и архивирование

Почему без правил LoRA быстро теряются

Первые две версии команда еще помнит. На третьей появляются файлы вроде support-final, support-final-2 и support-new. Еще через пару недель уже никто не скажет, какой адаптер обучали на свежих данных, какой копировали для теста, а какой случайно попал в прод.

Проблема начинается не с модели, а с привычки "разберемся потом". LoRA-адаптер хранит только веса. Если рядом нет понятного имени, версии, базовой модели, даты и короткого описания, файл быстро теряет смысл. Сначала команда тратит лишние 10 минут на поиск. Потом уже боится что-либо менять.

Обычно это выглядит так: путаются релизы, v3 и v4 никто не может быстро сравнить, адаптер цепляют к другой базе, а на простой вопрос "что сейчас стоит в проде?" нет уверенного ответа. После релиза становится еще хуже. Новую версию уже нельзя честно сравнить со старой, потому что непонятно, какую именно сборку брали за эталон.

Ошибка с базовой моделью особенно коварна. Адаптер может выглядеть "живым": он загружается, сервис отвечает, ничего не падает. Но качество уже едет - меняются тон, формат, точность и длина ответа. Если команда переключает базы через единый API-шлюз, такая путаница случается еще чаще: промпт тот же, имя адаптера знакомое, а база под ним уже другая.

Без истории изменений спор о качестве быстро превращается в угадайку. Один инженер уверен, что старая версия отвечала лучше. Другой смотрит на метрики и не понимает, что именно сравнивали. Менеджер видит жалобы пользователей, а команда тратит вечер не на исправление, а на поиск файла, который "точно где-то был".

Когда у адаптера нет правил жизни, он перестает быть артефактом релиза и становится просто папкой с непонятными весами. В этот момент даже хороший результат трудно повторить.

Что хранить рядом с адаптером

Одних весов мало. По самому файлу нельзя понять, на какой базе он работает, кто его собрал и можно ли выпускать его в прод.

Рядом с каждым адаптером держите короткий manifest в JSON или YAML. В нем должно быть точное имя базовой модели и ее ревизия. Недостаточно записать просто "Qwen" или "Llama". Нужен полный идентификатор, а если есть commit hash или revision id, то и он тоже. Даже близкие версии одной и той же базы могут вести себя по-разному.

Там же сохраните конфиг адаптера и параметры обучения: rank, alpha, target modules, длину контекста, learning rate, число эпох, версию датасета и seed. Если эти поля не записаны, вы уже не повторите обучение честно и не поймете, почему новая версия отвечает хуже или дороже.

Нужны и простые служебные данные: дата сборки, автор и описание задачи в одну-две строки. Например: "LoRA для маршрутизации обращений в поддержку банка по 12 категориям". Такая подпись полезнее длинного имени файла.

Не храните адаптер без быстрых проверок. Положите рядом несколько контрольных примеров, метрики и короткую заметку о провалах. Если адаптер путает два похожих класса или ломает формат ответа, команда должна увидеть это сразу, а не после релиза.

Статус версии тоже должен лежать рядом с артефактом, а не только в чате. Хватает простого набора: черновик, тест, прод, архив, откатан. Тогда любой инженер за минуту поймет, что можно катить дальше, а что трогать нельзя.

Если запросы идут через RU LLM, полезно сохранять и внутренний идентификатор адаптера или релизной связки, которая ушла в прод. Потом его проще сверить с аудит-трейлом и понять, какая версия отвечала пользователям в конкретный день.

Как назвать адаптер, чтобы не путаться

Если команда придумывает имена по настроению, через месяц никто не вспомнит, чем sales-final-new отличается от sales-latest-2. Лучше один раз принять общий шаблон и не менять его без причины.

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

Подойдет такой шаблон:

<задача>__<базовая_модель>__v<номер>

Например: support-faq__qwen2.5-7b-instruct__v003.

Такое имя читается без документации. Сразу видно, что адаптер обучали для FAQ, он привязан к конкретной базе, а версия идет по порядку. Если у вас несколько сред или заказчиков, добавьте только один дополнительный сегмент. Иначе имя станет длинным и неудобным.

Дата в имени нужна не всегда. Если релизы выходят редко, она только шумит. Если сборки появляются почти каждый день и команда часто сравнивает свежие версии, дата может помочь: support-faq__qwen2.5-7b-instruct__2025-04-27__v003. Но и в этом случае дата не заменяет номер версии.

Есть несколько маркеров, которые лучше запретить сразу: final, new, copy, latest. Они ничего не говорят о состоянии адаптера и ломают откат. Через две недели latest уже не latest, а final почти всегда оказывается предпоследним файлом.

Еще одно правило спасает от тихого хаоса: имя файла, имя в реестре и имя в деплой-конфиге должны совпадать символ в символ. Если файл называется support-faq__qwen2.5-7b-instruct__v003, не записывайте его в каталоге как faq-qwen-v3. На таких мелочах команды теряют часы, особенно когда нужно быстро вернуть прошлую версию.

Как проверять совместимость с базовой моделью

Сначала сверяйте не только название базы, но и точную ревизию. Адаптер для Llama 3.1 8B легко перепутать с соседней версией той же линейки. Ошибка всплывает не сразу: модель запускается, ответы приходят, но качество становится странным. Для каждой LoRA храните явную привязку к семейству, размеру и идентификатору базового чекпойнта.

Частая поломка выглядит буднично: файл лежит в правильной папке, имя похоже на правду, а внутри нет связи с конкретной базой. Эту связь лучше записывать в метаданные и не оставлять на память команды. Если база обновилась хотя бы на один релиз, совместимость нужно проверять заново.

Потом смотрите на формат весов и версии библиотек. Один и тот же адаптер может вести себя по-разному, если команда загружает его через другую версию Transformers, PEFT или иной формат весов. Если вы тестировали адаптер в safetensors с конкретной сборкой библиотек, это стоит зафиксировать рядом с артефактом.

С квантованием лучше не угадывать. Адаптер, который проверяли на полной или BF16-базе, не стоит без теста переносить на 4-bit или 8-bit вариант. Иногда он запускается без проблем, но качество плывет на длинных ответах, извлечении фактов или строгом формате JSON.

Перед релизом нужен короткий smoke test на одном и том же шаблоне промпта. Не меняйте системное сообщение, температуру и few-shot примеры между сравнениями. Иначе вы не поймете, что сломалось: адаптер или сам сценарий.

Быстрой проверки обычно хватает такой: сверить семейство модели, размер и ревизию базы, проверить формат весов и версии библиотек, убедиться в типе квантования и прогнать 5-10 коротких запросов на фиксированном шаблоне промпта.

Единый LLM-шлюз эти правила не отменяет. Даже если команда работает через RU LLM и не меняет SDK или промпты, адаптер все равно живет только рядом со своей базой, ее ревизией и повторяемым тестом.

Как выпускать новую версию

Быстрее разбирайте инциденты
Сверяйте ответы с аудит-трейлом и быстрее находите, что происходило на живых запросах.

Новый релиз начинается еще до обучения. Команда должна заранее зафиксировать, с какой базовой моделью будет работать адаптер, и не менять эту опору по ходу дела.

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

Дальше назначьте понятный номер версии и владельца релиза. К релизу хватит одной короткой заметки: что изменилось, что стало лучше и где есть риск. Перед выкладкой сделайте ручную проверку на 10-15 запросах. Возьмите обычные случаи, один сложный сценарий, один граничный и один заведомо плохой ввод.

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

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

Что проверить за пять минут до публикации

Перед релизом чаще всего ломается не сам адаптер, а учет. Файл есть, метрики есть, но через неделю никто не может быстро ответить, к какой базе он привязан, кто пустил его в прод и на что откатываться.

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

Короткий чек перед релизом

Сверьте имя версии во всех местах, где адаптер упоминается. Тег в хранилище, запись в реестре, имя в CI и релизная заметка должны совпадать посимвольно.

Проверьте карточку релиза. В ней должна быть указана точная базовая модель и ее ревизия, а не просто "Qwen 3" или "Llama 4".

Откройте результаты одного и того же тестового набора примеров. Не нужен большой отчет. Часто достаточно 10-20 типовых кейсов с ответами новой версии и прошлой рабочей версии, чтобы увидеть явную просадку или странный стиль.

Запишите, кто одобрил выпуск. Лучше указать конкретного человека или роль, а не абстрактную "команду ML".

Убедитесь, что прошлая стабильная версия лежит рядом и доступна без ручного поиска. Откат не должен требовать переписки в чате и подъема старых артефактов из архива.

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

Когда делать откат

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

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

Прошлую рабочую версию держите отдельно от черновиков и экспериментальных сборок. Удобно, когда в хранилище есть хотя бы три зоны: stable, release-candidate и draft. Тогда при проблеме команда не ищет "тот самый файл", а просто возвращает последний stable-адаптер, который уже прошел проверку.

Есть еще одно полезное правило: за один релиз меняйте только один параметр. Либо новый датасет, либо rank, либо alpha, либо набор системных инструкций для тестов. Если менять все сразу, потом почти невозможно понять, что именно испортило результат.

Каждый откат фиксируйте в журнале. Достаточно одной строки с временем, причиной, именем адаптера, версией базовой модели и ответственным человеком. Например: "2026-04-27 14:20, rollback to adapter-v12, причина: падение accuracy на 7%, ответственный: Иван П." Такой журнал экономит много времени при повторных сбоях.

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

Что архивировать, а что держать под рукой

Проверяйте прод по логам
RU LLM хранит логи и бэкапы в РФ, чтобы команда быстрее разбирала инциденты.

Если все версии лежат в одной папке, через месяц никто не вспомнит, какая из них рабочая, а какая была вечерним экспериментом. Поэтому сначала разделите активные адаптеры и историю. Рабочая зона нужна для того, что можно быстро выкатить или откатить. Архив - для того, что уже не участвует в обычной работе, но может понадобиться для аудита, разбора инцидента или повторного обучения.

В архив стоит класть не только веса. Без конфига и результатов тестов старый LoRA-адаптер почти бесполезен: файл есть, а понять, к какой базе он относился и как себя вел, уже нельзя. Нормальный архивный комплект включает сами веса, training config и параметры запуска, точную ссылку на базовую модель и ее ревизию, результаты проверок и запись в реестре с датой, автором и причиной загрузки.

Временные файлы рядом с продовыми версиями лучше не держать. Чекпоинты на каждом N-м шаге, кеши, логи отладки и разовые выгрузки быстро засоряют хранилище. Хуже то, что кто-то по ошибке может взять промежуточный файл и отправить его в прод.

Архивные версии не должны перезаписываться. Если команде нужно что-то поправить, она создает новую запись в реестре и новый объект в хранилище. Иначе теряется след изменений, а для расследования это плохой сценарий, особенно если адаптер уже работал с пользовательскими данными.

Срок хранения тоже лучше определить заранее. Черновики и промежуточные артефакты обычно можно удалять через 14-30 дней. Кандидаты на релиз часто держат дольше, например 90 дней. Продовые и архивные версии хранят по политике команды и требованиям комплаенса.

Если у вас есть LLM-реестр или шлюз вроде RU LLM, это разделение стоит закрепить не только в бакетах, но и в записях о версиях. Тогда рабочий путь и исторический путь не смешиваются уже на уровне процесса.

Частые ошибки в хранилище LoRA

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

Не менее частый сбой - сохранили только веса, а конфиг, датасет, commit базовой модели и параметры обучения потеряли. Такой файл потом почти бесполезен. Его можно загрузить, но нельзя быстро понять, с чем он совместим и почему он вообще давал хороший результат.

Еще один классический промах - перезаписали стабильную версию новым файлом с тем же именем. После этого откат превращается в ручное расследование по чатам, папкам и памяти инженеров.

Путаницу создают и имена вроде final, new, test2 или lora_good. Через месяц они не говорят вообще ничего. Отдельная ловушка - сравнивать качество на разных наборах примеров. Если один адаптер проверяли на 50 легких запросах, а другой на свежем наборе с трудными кейсами, вывод уже будет слабым.

Если убрать эти привычки, большая часть хаоса исчезает: не запускать адаптер без явной привязки к базовой модели, не хранить веса отдельно от метаданных, не перезаписывать стабильные файлы, не давать версиям пустые имена и не сравнивать релизы на разных тестах.

Простой сценарий из практики

Платите без наценки
RU LLM тарифицирует API по ставкам провайдеров без наценки на запросы.

У команды в ритейле есть LoRA для разбора обращений: "не пришел заказ", "нужен возврат", "сломался товар". Адаптер обучили на базе qwen3-8b-instruct, проверили на внутреннем наборе и выпустили как support-claims_v2025-04-12_lora3. В реестре рядом лежат хеш базовой модели, версия датасета, метрики и статус stable.

Через две недели команда обновляет базовую модель до нового релиза. Код не меняли, промпты тоже. Сначала все выглядит нормально, но потом в бою растет число странных ответов: часть запросов про возврат уходит в категорию доставки, а короткие жалобы модель начинает пересказывать вместо классификации.

Дальше спасает не память отдельных инженеров, а порядок в версиях. Дежурный открывает реестр и видит последнюю стабильную связку: qwen3-8b-instruct@2025-04-01 + support-claims_v2025-04-12_lora3. Он возвращает эту связку в прод, новый релиз базы помечают как blocked для этого адаптера, а команда фиксирует время инцидента и начинает разбор.

Через час сервис снова работает как раньше. На следующий день инженеры поднимают архив: старые прогоны, sample outputs и результаты проверки совместимости. Сравнение быстро показывает причину. Данные не испортились, и адаптер не "сломался сам". Проблему внесла смена базы: новый релиз иначе трактует короткие клиентские фразы, и LoRA, обученная на прежней базе, начинает чаще ошибаться.

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

Что делать дальше

Не пытайтесь сразу строить идеальное хранилище. Для начала хватит одного правила, которое команда примет без споров и начнет соблюдать уже на этой неделе. В теме LoRA дисциплина почти всегда полезнее сложной схемы.

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

После этого свяжите файлы с эксплуатацией. Если команда использует RU LLM, удобно привязать версию адаптера к журналу релизов и аудит-трейлу запросов. У сервиса это уже встроено, поэтому по инциденту проще увидеть, какой адаптер отвечал, на какой базовой модели он работал и кто перевел его в прод.

Этого уже достаточно, чтобы перестать искать нужную версию по папкам и перепискам. Следующий релиз пройдет спокойнее, а откат займет минуты, а не полдня.

Хранение LoRA-адаптеров: имена, откат и архивирование | RU LLM