Архив рубрики ~Лента новостей~

Эксперимент — как мы обучили 14B-модель за 42 минуты и перестали платить за API

Эксперимент — как мы обучили 14B-модель за 42 минуты и перестали платить за API
Эксперимент — как мы обучили 14B-модель за 42 минуты и перестали платить за API

Типичный путь внедрения языковой модели в компании почти всегда начинается с иллюзии простоты.

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

Реальность наступает через месяц эксплуатации в продакшене.

Контекст запроса раздувается до десяти тысяч токенов. Время генерации первого слова улетает за четыре секунды. Счет за вызовы API начинает пробивать триста-четыреста долларов в месяц на десяток активных сотрудников.

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

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

Это инженерное заблуждение.

В 2026 году связка из низкоранговой адаптации (LoRA), 4-битного квантования и кастомных ядер Unsloth позволяет дообучить открытую 14B-модель под узкую прикладную задачу на одной потребительской видеокарте за 40-50 минут. Затраты на электроэнергию или аренду облачной карты составляют буквально пару сотен рублей.

Ниже полный разбор этого эксперимента: от физики расхода видеопамяти и матричной факторизации до дневника отладки трех неудачных попыток и вывода готовой модели в рабочий контур.

Разбор инцидента — почему промпт проигрывает весам

Посмотрим на конкретный пример из нашей практики: генерация аналитических SQL-запросов к распределенной базе ClickHouse по сложным вопросам аналитиков.

Кстати, если вы хотите протестировать все самые свежие модели уровня Claude Sonnet 5, GPT-5.6 или Gemini 3 в одном удобном месте и сравнить их ответы без костылей с иностранными картами — на платформе SYNTX.AI это можно сделать за пару кликов.

По промокоду NEIROSKUF дадут 15% скидку на все тарифы.

Базовая модель Llama 3 70B через облачный API с подробным системным промптом на описание таблиц выдает следующий код:

SELECT user_id, count(*) as events FROM user_logs WHERE event_date >= '2026-05-01' GROUP BY user_id HAVING count(*) > 10;

Запрос выглядит синтаксически чистым, но при выполнении в СУБД падает с ошибкой. В реальной базе колонка с датой называется event_timestamp и имеет тип DateTime64(3), а таблицы user_logs не существует вовсе: аналитические события лежат в распределенной таблице events_v2 внутри кластера. Модель просто подставила типовые стандартные названия из своего открытого претрейна.

А вот результат работы компактной модели Qwen 2.5 14B, которую мы дообучили на шестистах реальных примерах наших аналитиков:

SELECT user_id, count() AS total_events FROM clusterAllReplicas('company_cluster', default.events_v2) WHERE event_timestamp >= toDateTime64('2026-05-01 00:00:00', 3) GROUP BY user_id HAVING total_events > 10;

Модель с первого раза использовала специфическую кластерную функцию clusterAllReplicas, указала правильное имя кластера, применила внутреннюю функцию парсинга дат ClickHouse и корректно отфильтровала партиции.

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

Физика видеопамяти — почему стандартный PyTorch требует суперкомпьютер

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

Для монолитной модели размером 14 миллиардов параметров в 16-битном формате (FP16/BF16) базовый вес составляет 28 гигабайт. При классическом полном дообучении (Full Fine-Tuning) видеопамять съедают четыре компонента:

  1. Веса модели: 28 ГБ.
  2. Градиенты: еще 28 ГБ для хранения производных по каждому параметру.
  3. Состояния оптимизатора AdamW: оптимизатор хранит два дополнительных 32-битных числа для каждого параметра (первый момент — среднее градиента, и второй момент — дисперсию). Это требует еще 56 гигабайт памяти.
  4. Матрица логитов при расчете функции потерь: самое коварное узкое место.

В стандартной реализации PyTorch при расчете ошибки Cross-Entropy модель берет скрытое состояние последнего слоя и умножает его на матрицу эмбеддингов, материализуя в видеопамяти полный массив вероятностей для каждого токена словаря. При словаре в 128 000 токенов, длине контекста 2048 и батче 2 этот временный тензор логитов съедает более двадцати гигабайт VRAM за доли секунды.

Суммарно для запуска стандартного обучения 14B-модели требуется более 140 гигабайт видеопамяти. Это заставляет арендовать кластеры из двух или четырех карт A100/H100.

Компиляторный прорыв: как ядра Triton в Unsloth сжимают вычисления

Фреймворк Unsloth решил проблему расхода памяти не за счет компромиссов по качеству, а через переписывание низкоуровневых вычислительных ядер на языке OpenAI Triton.

Главная оптимизация затронула расчет функции потерь. Вместо материализации гигантского массива логитов на 128 000 элементов в глобальной видеопамяти (HBM), кастомное ядро Unsloth вычисляет градиенты по частям прямо в сверхбыстрой разделяемой памяти процессора (SRAM). Память под массив логитов не выделяется вовсе.

Второй рубеж экономии — 4-битное квантование базовой модели в формате NormalFloat4 (NF4). Базовые 14 миллиардов параметров замораживаются и сжимаются до 8.5 гигабайт.

В итоге для всего процесса обучения 14B-модели требуется около 11-12 гигабайт видеопамяти. Процесс спокойно помещается в потребительскую видеокарту уровня RTX 3060 12GB, RTX 4070 или бесплатный инстанс Google Colab.

Принцип низкоранговой адаптации (LoRA)

Вместо того чтобы обновлять все 14 миллиардов весов, мы применяем метод Low-Rank Adaptation.

Математический фундамент метода прост: при адаптации предобученной нейросети под конкретную задачу реальное пространство необходимых изменений имеет крайне низкую размерность.

Пусть W0 — замороженная матрица исходного слоя трансформера. Мы представляем изменение веса как произведение двух компактных матриц:

dW = B * A

где матрица A инициализируется случайными числами, а матрица B — нулями. Ранг r (внутренняя размерность) выбирается во много раз меньше размера слоя (например, r = 16).

В момент старта обучения произведение B * A строго равно нулю, поэтому начальное поведение модели не нарушается.

При прямом проходе сигнал вычисляется по формуле:

h = W0 * x + (alpha / r) * (B * A) * x

Параметр alpha задает коэффициент масштабирования адаптера. Мы используем проверенное правило: alpha = 2 * r. При ранге r = 16 коэффициент масштабирования равен 32.

Для слоя внимания размером 4096 на 4096 количество параметров уменьшается с 16.7 миллионов до 131 тысячи. Оптимизатор AdamW хранит состояния только для этих микроскопических матриц, что снижает расход памяти на оптимизатор с 56 гигабайт до пары сотен мегабайт.

Критически важный нюанс: адаптеры обязаны подключаться ко всем семи линейным проекциям трансформера (q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj). Обучение одних лишь слоев внимания позволяет модели усвоить форму ответа, но оставляет пробелы в запоминании новых фактических связей, которые хранятся в блоках многослойного перцептрона (MLP).

Наука о данных: почему 500 примеров бьют 50 000

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

В исследовании Meta под названием LIMA (Less Is More for Alignment) исследователи доказали фундаментальный принцип: качество финальной модели определяется не объемом данных, а их чистотой и разнообразием. Одна тысяча выверенных вручную примеров дает устойчивость генерации выше, чем 50 000 сырых нефильтрованных строк.

Для прикладной задачи достаточно подготовить от 400 до 800 качественных примеров.

Датасет оформляется в виде стандартного JSON-файла с диалогами:

[ { "messages": [ { "role": "user", "content": "Посчитай количество уникальных пользователей, совершивших покупку на сумму более 5000 рублей в марте 2026 года." }, { "role": "assistant", "content": "SELECT uniqExact(user_id) AS paying_users FROM clusterAllReplicas('company_cluster', default.orders) WHERE status = 'completed' AND total_amount > 5000 AND order_date >= '2026-03-01' AND order_date < '2026-04-01';" } ] } ]

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

Дневник отладки: три сбоя перед идеальным результатом

Мы собрали 600 эталонных пар «вопрос аналитика — SQL-запрос» и запустили процесс дообучения.

Вот через какие практические грабли пришлось пройти:

Сбой 1. Взрыв градиентов при завышенном Learning Rate

В первом запуске мы выставили скорость обучения learning_rate = 0.001 (1e-3), посчитав, что для быстрой сходимости на маленьком датасете нужен высокий темп.

На 14-м шаге обучения функция потерь совершила резкий скачок с 2.1 до 14.8, после чего значение упало в NaN. Матрицы адаптера переполнились, градиенты взорвались, и модель на любой вход стала генерировать бесконечный цикл из символов !!!.

Решение: Скорость обучения для LoRA обязана лежать в диапазоне от 0.0001 (1e-4) до 0.00025 (2.5e-4). Мы зафиксировали learning_rate = 0.0002 (2e-4), включили косинусное расписание спада (cosine decay) и добавили 10 шагов плавного прогрева (warmup steps). График ошибки стабилизировался.

Сбой 2. Обучение только матриц внимания

Во втором прогоне мы ради экономии времени подключили адаптеры только к матрицам q_proj и v_proj, как рекомендовали старые статьи 2022 года.

Модель сошлась быстро, а значение ошибки упало до 0.38. Однако при тестировании на контрольных вопросах выяснилось, что модель отлично строит общую структуру запроса (SELECT … GROUP BY), но путает специфические функции ClickHouse (например, пишет стандартный COUNT(DISTINCT user_id) вместо быстрого uniqExact(user_id)).

Решение: Добавление адаптеров на все слои MLP (gate_proj, up_proj, down_proj). Размер файла адаптера вырос с 60 до 150 мегабайт, но модель полностью зафиксировала внутренний диалект базы.

Сбой 3. Рассинхронизация шаблона токенизатора

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

Причина оказалась в токенизации: при подготовке датасета функция форматирования использовала стандартные текстовые разделители User: и Assistant:, в то время как базовая модель Qwen 2.5 использует специальный системный шаблон ChatML с управляющими токенами <|im_start|> и <|im_end|>. Модель во время обучения видела один формат разметки, а в продакшене получала другой.

Решение: Использование строго нативного метода tokenizer.apply_chat_template() при предобработке датасета. Модель сразу начала отвечать идеально.

Запускаемый скрипт обучения на Python

Вот полный и проверенный рабочий код, который выполняет весь цикл тренировки:

import torch from unsloth import FastLanguageModel from datasets import load_dataset from trl import SFTTrainer from transformers import TrainingArguments # 1. Загрузка базовой модели в 4-битном формате max_seq_length = 2048 model, tokenizer = FastLanguageModel.from_pretrained( model_name="Qwen/Qwen2.5-Coder-14B-Instruct", max_seq_length=max_seq_length, load_in_4bit=True, ) # 2. Подключение LoRA ко всем семи линейным слоям model = FastLanguageModel.get_peft_model( model, r=16, target_modules=[ "q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj", ], lora_alpha=32, lora_dropout=0, bias="none", use_gradient_checkpointing="unsloth", ) # 3. Подготовка датасета с применением нативного шаблона ChatML dataset = load_dataset("json", data_files="dataset.json", split="train") def format_conversations(batch): formatted_texts = [ tokenizer.apply_chat_template(dialogue, tokenize=False, add_generation_prompt=False) for dialogue in batch["messages"] ] return {"text": formatted_texts} dataset = dataset.map(format_conversations, batched=True) # 4. Настройка параметров тренировки trainer = SFTTrainer( model=model, tokenizer=tokenizer, train_dataset=dataset, dataset_text_field="text", max_seq_length=max_seq_length, args=TrainingArguments( per_device_train_batch_size=2, gradient_accumulation_steps=4, warmup_steps=10, max_steps=150, # Около 2.5 эпох на 600 примерах learning_rate=2e-4, fp16=not torch.cuda.is_bf16_supported(), bf16=torch.cuda.is_bf16_supported(), logging_steps=10, optim="adamw_8bit", weight_decay=0.01, lr_scheduler_type="cosine", output_dir="training_output", seed=42, ), ) # 5. Запуск обучения trainer.train() # 6. Экспорт в квантованный формат GGUF для продакшена model.save_pretrained_gguf("qwen_clickhouse_14b_q4", tokenizer, quantization_method="q4_k_m")

Результаты в цифрах и эксплуатация

Обучение на видеокарте RTX 4070 Ti заняло ровно 42 минуты.

Значение функции потерь снизилось с начальных 2.38 до 0.27 на финальном шаге. На тестовой выборке из 100 контрольных задач, которые модель не видела во время обучения, точность генерации составила 98%.

Итоговый файл qwen_clickhouse_14b_q4.gguf весит 8.6 гигабайт. Мы развернули его на сервере с видеокартой RTX 3060 12GB под управлением vLLM.

Эксплуатационные показатели:

  • Скорость генерации около 46-50 токенов в секунду.
  • Время формирования сложного аналитического SQL — менее одной секунды.
  • Зависимость от внешних облачных API — ноль.
  • Ежемесячные затраты на токены — ноль рублей.

Эра слепой переплаты за облачные токены пока еще не подошла к концу, но что-то под себя сделать уже вполне возможно.

Эксперимент - как мы обучили 14B-модель за 42 минуты и перестали платить за API
1telegram.metelegram.me

Источник: vc.ru

Оцените материал:

Поделиться
Понравилась статья? Расскажите другим
ВКонтакте
Читайте также
Новости робототехники ДТП с беспилотными автомобилями?? Архив рубрики ~Коротко из Telegram~ Perplexity запустила локальную версию агентного ИИ Portable Computer Perplexity представила… Новости робототехники Один мозг на всех Одна и та же команда для… Архив рубрики ~Коротко из Telegram~ Интересной дорогой идем мы с этими роботами. Информационное поле разделилось… Архив рубрики ~Коротко из Telegram~ Каждый раз с появлением афиши предстоящей презентации #AppleEvent 📘 пользователи… Архив рубрики ~Обо всем~ Более быстрый путь Маска к увеличению количества газовых турбин сопряжен с проблемой загрязнения окружающей среды. Архив рубрики ~Коротко из Telegram~ Минцифры предложило запустить 5G во всех городах-миллионниках не позднее 31… Архив рубрики ~Коротко из Telegram~ Технодайджест недели В Пекине завершились вторые Всемирные игры гуманоидных роботов…. Архив рубрики ~Коротко из Telegram~ Одноразовая ПОЧТА за пару кликов — забываем про спам и… Архив рубрики ~Коротко из Telegram~ Нейросети доросли до бесконечного ИИ-телеканала с «Риком и Морти». Разработчик… Архив рубрики ~Коротко из Telegram~ Huawei придумали смартфон для людей, которые устали тянуть большой палец… Архив рубрики ~Коротко из Telegram~ GLM-5.3 наконец выпустили в открытый доступ. Это одна из сильнейших… Архив рубрики ~Коротко из Telegram~ Midjourney наконец-то заходит на территорию нормального image editing. Сегодня компания… Архив рубрики ~Коротко из Telegram~ Разработчик посадил ChatGPT за виртуальный ноутбук Вайб-кодер Джонатан Ма решил,… Новости робототехники ДТП с беспилотными автомобилями?? Архив рубрики ~Коротко из Telegram~ Perplexity запустила локальную версию агентного ИИ Portable Computer Perplexity представила… Новости робототехники Один мозг на всех Одна и та же команда для… Архив рубрики ~Коротко из Telegram~ Интересной дорогой идем мы с этими роботами. Информационное поле разделилось… Архив рубрики ~Коротко из Telegram~ Каждый раз с появлением афиши предстоящей презентации #AppleEvent 📘 пользователи… Архив рубрики ~Обо всем~ Более быстрый путь Маска к увеличению количества газовых турбин сопряжен с проблемой загрязнения окружающей среды. Архив рубрики ~Коротко из Telegram~ Минцифры предложило запустить 5G во всех городах-миллионниках не позднее 31… Архив рубрики ~Коротко из Telegram~ Технодайджест недели В Пекине завершились вторые Всемирные игры гуманоидных роботов…. Архив рубрики ~Коротко из Telegram~ Одноразовая ПОЧТА за пару кликов — забываем про спам и… Архив рубрики ~Коротко из Telegram~ Нейросети доросли до бесконечного ИИ-телеканала с «Риком и Морти». Разработчик… Архив рубрики ~Коротко из Telegram~ Huawei придумали смартфон для людей, которые устали тянуть большой палец… Архив рубрики ~Коротко из Telegram~ GLM-5.3 наконец выпустили в открытый доступ. Это одна из сильнейших… Архив рубрики ~Коротко из Telegram~ Midjourney наконец-то заходит на территорию нормального image editing. Сегодня компания… Архив рубрики ~Коротко из Telegram~ Разработчик посадил ChatGPT за виртуальный ноутбук Вайб-кодер Джонатан Ма решил,…

Оставить комментарий