Модуль 11. Своя open-weights модель: QLoRA в DataSphere
Цель модуля: дообучить открытую модель (Qwen, Llama) в ноутбуке DataSphere и забрать результат с собой: настроить GPU-конфигурацию, собрать QLoRA-пайплайн на transformers + PEFT, сохранить адаптер в Object Storage, проверить модель инференсом и честно спланировать деплой и бюджет в юнитах.
Перед началом: модуль 6 (LoRA/QLoRA и VRAM-бюджеты), модуль 7 (гиперпараметры), модуль 9 (DataSphere, квоты, «ноутбук выключай»). Инструменты: DataSphere-ноутбук с GPU. Нужен аккаунт YC с грантом. Трудоёмкость: ~2.5 ч руками + время обучения (зависит от конфигурации).
🔥 URI есть — весов нет
Модуль 10 закончился победой: дообученная модель отвечает изящнее базы. Потом приходит вопрос от безопасности: «А где лежат веса? Мы хотим модель в закрытом контуре». И вопрос от продукта: «А можно Qwen? У него русский лучше в нашем домене». На оба ответ один: в AI Studio — никак. Дообученный YandexGPT существует только как URI в облаке Яндекса.
Open-weights мир отвечает иначе: скачиваете Qwen или Llama с HuggingFace, дообучаете QLoRA-адаптер (модуль 6) на своей GPU-конфигурации, сохраняете результат в свой Object Storage. Адаптер — десятки мегабайт — применяется к базовой модели где угодно: в другом облаке, на своём сервере, в закрытом контуре. Цена — ответственность за конфигурацию, память и деплой ваша.
📖 11.1. Когда этот путь ваш
Честный список причин идти в DataSphere за open-weights:
- Нужна другая база. Qwen, Llama, более свежая/меньшая/большая модель, чем YandexGPT Lite. В AI Studio выбор фиксирован.
- Контроль и переносимость. Закрытый контур, on-premise завтра, смена облака без переобучения. Адаптер переносится везде, где есть база с HF.
- Свобода и исследование. Свои гиперпараметры, свои слои для LoRA, ранняя остановка по валидации (модуль 7), сравнение баз методично.
И цена: вы сами выбираете конфигурацию и платите юниты за её время (модуль 9), сами ловите OOM, сами деплоите. Если задача решается тюнингом модуля 10 — она решается дешевле.
📖 11.2. Ноутбук и конфигурация: gt4.1 или g1.1/g2.1
Создайте проект в DataSphere и ноутбук в нём, добавьте секреты
FOLDER_ID/API_KEY из модуля 9 (пригодятся для Object Storage) и выберите
конфигурацию вычислений — главное денежное решение модуля.
| Модель (QLoRA) | Конфигурация | Комментарий |
|---|---|---|
| 3–4B (Qwen2.5-3B) | gt4.1 (T4, 16 ГБ) | дешёвый вход; наш путь по умолчанию |
| 7–8B (Llama-3.1-8B) | g1.1 (V100, 32 ГБ) | 16 ГБ для 7B — «впритык» (модуль 6) |
| 7–8B + длинный контекст | g2.1 (A100, 80 ГБ) | быстро и свободно, но дороже всех |
Полный список конфигураций — в документации: https://yandex.cloud/ru/docs/datasphere/concepts/configurations. Квоты каталога проверьте до старта (модуль 9): на GPU-конфигурации по умолчанию часто 0–1.
И правило модуля 9, повторённое осознанно: закончили обучение — останавливайте конфигурацию; индикатор проверяется перед каждым чаепитием.
📖 11.3. Скелет QLoRA-ноутбука
Каркас из семи ячеек. Код не исполняется в тексте модуля — соберите его в DataSphere; теория шагов — модуль 6 (4 бит и ΔW=A·B) и модуль 7.
python# Ячейка 1 — зависимости (экосистема HuggingFace для QLoRA) %pip install -q -U transformers datasets peft trl accelerate bitsandbytes
python# Ячейка 2 — базовая модель в 4 бит: QLoRA из модуля 6 import torch from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig MODEL_ID = "Qwen/Qwen2.5-3B-Instruct" # под gt4.1; для 7B см. таблицу выше bnb = BitsAndBytesConfig( load_in_4bit=True, # базовые веса квантуются в NF4 bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.bfloat16, ) tokenizer = AutoTokenizer.from_pretrained(MODEL_ID) model = AutoModelForCausalLM.from_pretrained( MODEL_ID, quantization_config=bnb, device_map="auto", )
python# Ячейка 3 — LoRA-конфиг: учим только дельту A·B, базу не трогаем from peft import LoraConfig, get_peft_model lora_cfg = LoraConfig( r=16, lora_alpha=32, # ранг и масштаб (модули 6–7) target_modules=["q_proj", "k_proj", "v_proj", "o_proj"], lora_dropout=0.05, task_type="CAUSAL_LM", ) model = get_peft_model(model, lora_cfg) model.print_trainable_parameters() # ожидаем доли процента всех весов
python# Ячейка 4 — датасет + chat template (модули 3 и 5: формат решает!) from datasets import load_dataset ds = load_dataset("json", data_files="train.jsonl", split="train") def to_chat(example): msg = [{"role": "user", "content": example["request"]}, {"role": "assistant", "content": example["response"]}] return {"text": tokenizer.apply_chat_template(msg, tokenize=False)} ds = ds.map(to_chat) print(ds[0]["text"]) # ОБЯЗАТЕЛЬНО посмотрите глазами: спецтокены на месте?
python# Ячейка 5 — SFTTrainer: те же гиперпараметры, что в модуле 7 from trl import SFTConfig, SFTTrainer cfg = SFTConfig( output_dir="checkpoints", per_device_train_batch_size=1, # OOM? первым делом уменьшить батч gradient_accumulation_steps=16, # эффективный батч = 1 × 16 gradient_checkpointing=True, # память в обмен на скорость max_seq_length=512, # OOM? резать длину последовательностей num_train_epochs=2, # в LLM epochs = 1–3, не 100 (модуль 7) learning_rate=2e-4, # типичный диапазон LoRA: 1e-4…2e-4 logging_steps=10, save_steps=100, ) trainer = SFTTrainer(model=model, args=cfg, train_dataset=ds, processing_class=tokenizer) trainer.train() # loss виден прямо в выводе ячейки
python# Ячейка 6 — сохраняем адаптер и выгружаем в Object Storage (S3-совместимый) import os import boto3 model.save_pretrained("adapter") # адаптер — мегабайты, не вся модель s3 = boto3.client( "s3", endpoint_url="https://storage.yandexcloud.net", aws_access_key_id=os.environ["AWS_ACCESS_KEY_ID"], # статический aws_secret_access_key=os.environ["AWS_SECRET_ACCESS_KEY"],# ключ бакета ) s3.upload_file("adapter/adapter_model.safetensors", "llm-course-bucket", "adapters/style-v1/adapter_model.safetensors")
python# Ячейка 7 — инференс прямо в ноутбуке: проверка до всякого деплоя messages = [{"role": "user", "content": "Купил курс, не зашло. Как вернуть деньги?"}] text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True) inputs = tokenizer(text, return_tensors="pt").to(model.device) out = model.generate(**inputs, max_new_tokens=200) print(tokenizer.decode(out[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True))
Почему save_steps=100: чекпойнты позволяют откатиться к середине обучения,
если финал переобучился (модуль 7). Выгрузили адаптер в бакет — ноутбук можно
выключать: результат переживёт проект. Пример пайплайна от Yandex Cloud:
https://github.com/yandex-cloud-examples/yc-foundation-models-datasphere-tuning.
📖 11.4. Альтернативы: LLaMA-Factory и Unsloth
Скелет из 11.3 — «ручной» путь: максимум контроля; есть и готовые обёртки:
- LLaMA-Factory (https://github.com/hiyouga/LLaMA-Factory) — веб-интерфейс над тем же стеком: датасет, модель и LoRA-параметры выбираются в UI; хорош для серии однотипных экспериментов.
- Unsloth (https://github.com/unslothai/unsloth) — оптимизированная реализация QLoRA: быстрее и экономнее по памяти на тех же задачах.
Рекомендация курса: первый прогон сделайте руками (11.3) — поняв BitsAndBytes → LoRA → SFTTrainer, вы будете видеть, что делает обёртка.
Отдельный вариант «вообще без облака» — на случай «нет гранта» или
«закрытый контур». Обучение QLoRA идёт на своей машине (или в Colab), а
инференс дообученной модели удобно поднять на Ollama: ollama pull qwen2.5:3b (≈ 2 ГБ), и на localhost:11434 появляется OpenAI-совместимый
эндпоинт — тот же контракт, что у AI Studio, ваш клиент не меняется. В
одном рабочем сервисе-оркестраторе так и живёт локальный контур целиком:
Ollama зафиксированной версии в docker-compose, модель в docker-volume,
никаких ключей и счетов. Честный минус — скорость: без GPU инференс идёт
на CPU (в Docker на macOS GPU недоступен), 3B-модель терпимо, 7B «в разы
медленнее». И практическая деталь: версию инференс-сервера фиксируют —
API Ollama дрейфует между версиями (формат даже /api/tokenize менялся),
и код, работающий сегодня, после обновления может сломаться. Тот же принцип
immutable-версий, что в модуле 12, только для софта, а не для модели.
📖 11.5. Типовые ошибки и их симптомы
- OOM (CUDA out of memory). Падает на первом же длинном примере. Лестница
спасения: батч → 1;
max_seq_lengthвниз (512 → 256); проверитьgradient_checkpointing=True; модель поменьше или конфигурация побольше. Батч 1 + аккумуляция 16 — норма, а не поражение. - Забытый GPU-рантайм. Ноутбук на CPU-конфигурации: обучение «зависает» — шаги идут, но невыносимо медленно; проверьте селектор конфигурации. Обратная беда из модуля 9: обучение закончилось, а конфигурация работает — юниты капают. Остановите.
- Кривой chat template. Пропустили
apply_chat_templateили взяли датасет с чужой разметкой диалога — модель учит неправильные спецтокены: loss падает, а в бою (ячейка 7) ответы бессвязные, в текст лезет<|im_start|>. Лечение: ячейка 4 печатает пример глазами. - Проверка вслепую. Только train loss — как в модуле 7: добавьте валидацию, финал оценивайте golden set-ом из модуля 8 («loss упал» ≠ «стало лучше»).
- Чекпойнт только в ноутбуке. Не сохранили в Object Storage, проект почистили — работа пропала. Ячейка 6 — не опция.
📖 11.6. Деплой честно и бюджет в юнитах
Деплой. В Yandex Cloud нет managed-инференса для собственных весов — сервиса «загрузите адаптер — получите API» не существует (в отличие от URI из модуля 10). Варианты, оба рабочие:
- ВМ с GPU и vLLM/Ollama. Виртуальная машина с GPU, база + адаптер — свой API-сервер в вашем облаке.
- Перенос адаптера. Адаптер — десятки мегабайт, база открыта: деплой где угодно — другой кластер, свой сервер, закрытый контур (применение — две строки PEFT, как в ячейке 7); минимальный вариант — локальный Ollama из 11.4: та же база + адаптер, эндпоинт на localhost.
Бюджет на 7B QLoRA — прикидка из трёх чисел (ставки смотрите в документации DataSphere, они меняются):
textшаги ≈ (пар в датасете × эпох) / эффективный батч 200 пар × 2 эпохи / 16 ≈ 25 шагов → минуты на g1.1 (тестовый прогон) 2000 пар × 3 эпохи / 16 ≈ 375 шагов → единицы часов на g1.1 итог = часы × ставка конфигурации в юнитах × 2 (отладка, повторы, инференс)
Главный вывод: тестовый прогон на 200 парах стоит копейки — гоняйте им весь пайплайн до «большого» обучения, ошибки из 11.5 дешевле ловить на малом.
📖 11.7. Компромиссы
- gt4.1 vs g1.1/g2.1. Дешёвая T4 требует модель поменьше и учит дольше; A100 ускоряет за юниты. Отладка — на дешёвой, финал — по размеру задачи.
- 3–4B vs 7–8B. Меньшая модель дешевле в обучении и деплое, но может не дотянуть по качеству домена; проверяйте на golden set (модуль 8).
- Ручной стек vs LLaMA-Factory/Unsloth. Руки дают понимание и гибкость, обёртки — скорость экспериментов; после ручного прогона обёртка не магия.
- Свой деплой (ВМ+GPU) vs адаптер на сторону. Свой API в YC — всё в одном облаке, но GPU-инстанс в юнитах круглосуточно; перенос адаптера почти бесплатен, но инфраструктура ваша.
- Ноутбук vs скрипт. Ноутбук идеален для исследования; воспроизводимый пайплайн (капстоун, модуль 13) — скрипт с фиксацией версий библиотек.
💻 Практика
Нужен аккаунт Yandex Cloud с грантом; квота на выбранную конфигурацию проверена (модуль 9).
- Проект и окружение (15 мин). Создайте проект DataSphere, добавьте секреты, создайте ноутбук. Ячейки установки погоняйте на дешёвой CPU-конфигурации: код должен падать до включения GPU.
- Датасет (10 мин). Тот же
train.jsonlиз модулей 5 и 10 — своя база не отменяет гигиену данных. - Скелет на 3–4B (30 мин). Ячейки 1–7 на gt4.1 и модели 1–3B (Qwen2.5-1.5B/3B-Instruct): цель — не качество, а работающий конвейер до чекпойнта в Object Storage.
- Обучение и кривые (30 мин + ожидание). Запустите ячейку 5; следите за loss (модуль 7). Остановите конфигурацию сразу после завершения.
- Инференс и оценка (20 мин). Ячейка 7 — ответы глазами; затем 10–20 кейсов golden set (модуль 8) против базовой Qwen без адаптера.
- Артефакты (10 мин). Адаптер в бакете; таблица версий пополнена: модель, конфигурация, часы, юниты из биллинга, метрика на golden set.
- Бюджет (10 мин). По формуле 11.6 прикиньте стоимость «настоящего» прогона на вашем объёме данных и сверьте с остатком гранта.
🧩 Quiz "Проверь себя"
✅ Итоги модуля
- Назвать случаи, когда open-weights выгоднее тюнинга AI Studio (модуль 10), и выбрать конфигурацию под размер модели (gt4.1 для 3–4B, g1.1/g2.1 для 7–8B) по VRAM-бюджету модуля 6.
- Собрать скелет QLoRA: BitsAndBytesConfig → LoraConfig → датасет с chat template → SFTTrainer.
- Прогнать тестовое обучение, остановить конфигурацию, проверить биллинг.
- Сохранить адаптер в Object Storage и проверить инференсом в ноутбуке.
- Оценить бюджет по формуле 11.6 и назвать варианты деплоя своих весов.
🔗 Мостик к Модулю 12
Модель готова и где-то отвечает: URI в AI Studio или адаптер на ВМ с GPU. Осталась эксплуатация: вызов из бэкенда с таймаутами и ретраями, версионирование («какая версия была в проде месяц назад?»), выкат shadow → A/B, fallback-цепочка, чтобы деградация модели не роняла фичу, и сбор фидбека в топливо для следующего датасета. Всем этим — с Go-клиентом и мок-тестом — занимается модуль 12.