← НАЗАД К КУРСУ Этап 5 · Урок 3 из 5

Урок 5.3 — Fine-tuning: как дообучить модель под свои задачи

Claude знает миллионы фактов, может рассуждать и писать код. Но он не знает, как именно твоя компания отвечает клиентам, какие категории обращений у тебя есть и в каком формате должен быть отчёт. В уроке 5.1 мы решали это через RAG — подкладывали нужные документы в промпт. Fine-tuning решает это иначе: мы берём готовую модель и дообучаем её на своих примерах. После дообучения модель «знает» твой стиль, формат и терминологию — без длинных инструкций в каждом запросе.


Проблема: промпт не всегда достаточно

В уроке 2.4 (Prompt Engineering) мы научились писать хорошие инструкции для модели. Это работает для большинства задач. Но есть ситуации, когда даже идеальный промпт не справляется:

Ситуация 1: Нестабильный формат
  Промпт: "Отвечай строго в формате JSON с полями category, sentiment, priority"
  Реальность: в 90% случаев модель слушается. В 10% — добавляет пояснения,
  меняет названия полей или забывает про JSON.

Ситуация 2: Длинные повторяющиеся инструкции
  Каждый запрос начинается с 500 токенов инструкций:
  "Ты — классификатор обращений. Категории: возврат, доставка, оплата..."
  При 10 000 запросов в день — 5 000 000 лишних токенов. Это деньги.

Ситуация 3: Специфический стиль
  Промпт: "Отвечай дружелюбно, коротко, с конкретными шагами"
  Реальность: модель понимает "дружелюбно" по-своему.
  А нужно — точно как в 500 реальных ответах службы поддержки.

Ситуация 4: Специальная терминология
  Модель пишет "заболевание сердца" вместо "I25.1 по МКБ-10".
  Медицинская, юридическая, финансовая терминология —
  модель знает её, но не использует по умолчанию в нужном формате.

Все эти проблемы объединяет одно: модели нужно изменить поведение, а не дать новые факты. RAG даёт факты. Fine-tuning меняет поведение.


Что такое fine-tuning

Fine-tuning (дообучение) — процесс, при котором готовую модель дополнительно тренируют на твоих примерах. Модель не учится с нуля. Она уже умеет понимать язык, рассуждать, генерировать текст. Fine-tuning добавляет поверх этого новый навык — как второй слой знаний.

Базовая модель (GPT-4o, Claude, Llama)
  Умеет: понимать язык, рассуждать, отвечать на вопросы, писать код
  Не умеет: писать в стиле твоей компании, классифицировать
            по твоим категориям, следовать твоему формату

         │
         │  Fine-tuning
         │  (показываем 200-5000 примеров "вход → правильный выход")
         ▼

Дообученная модель
  Умеет всё то же самое + новый навык:
  ✓ Пишет в стиле твоих документов
  ✓ Классифицирует по твоим категориям
  ✓ Отвечает в точном формате без напоминаний

Аналогия

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

Fine-tuning — это стажировка для модели. Базовые знания остаются. Добавляется специализация.


Три способа адаптировать модель

В курсе мы изучили три подхода. Каждый решает свою задачу:

┌──────────────────────────────────────────────────────────────────┐
│              ТРИ СПОСОБА АДАПТИРОВАТЬ МОДЕЛЬ                     │
│                                                                  │
│  1. Промпт-инженерия (урок 2.4)                                 │
│     Что делаем: пишем подробную инструкцию в промпте            │
│     Что меняется: контекст запроса                              │
│     Модель: остаётся той же                                     │
│     Данные: в промпте (каждый раз)                              │
│     Обновление: мгновенно — поменял промпт и готово             │
│     Стоимость: бесплатно (но длинный промпт = больше токенов)   │
│                                                                  │
│  2. RAG (урок 5.1)                                               │
│     Что делаем: ищем нужные данные и вкладываем в промпт        │
│     Что меняется: контекст запроса (динамически)                │
│     Модель: остаётся той же                                     │
│     Данные: в векторной базе (обновляются мгновенно)            │
│     Обновление: добавил документ в базу — сразу доступен        │
│     Стоимость: база + embeddings + токены контекста              │
│                                                                  │
│  3. Fine-tuning (этот урок)                                      │
│     Что делаем: дообучаем модель на своих примерах              │
│     Что меняется: сама модель (её параметры)                    │
│     Модель: создаётся новая версия                              │
│     Данные: «вшиты» в модель при обучении                       │
│     Обновление: нужно переобучать (часы + деньги)               │
│     Стоимость: обучение + использование дороже базовой модели   │
│                                                                  │
└──────────────────────────────────────────────────────────────────┘

Аналогия: три способа научить сотрудника

Промпт-инженерия = должностная инструкция
  Каждое утро даёшь сотруднику памятку: "Отвечай так-то, формат такой-то."
  Работает, но памятку нужно давать каждый раз.

RAG = справочник на столе
  Сотрудник не помнит все цены и правила — но у него есть справочник.
  Когда клиент спрашивает — он находит ответ и говорит.
  Справочник можно обновить в любой момент.

Fine-tuning = обучение и стажировка
  Сотрудник прошёл тренинг: 500 примеров, как отвечать клиентам.
  Теперь он делает это автоматически, без памятки и справочника.
  Но если правила поменялись — нужен новый тренинг.

Дерево выбора: что использовать

Задача решается хорошим промптом (с примерами)?
│
├── ДА → Промпт-инженерия. Не усложняй.
│
└── НЕТ → Модели нужны конкретные факты или документы?
          │
          ├── ДА → RAG. Данные в базе, обновляются мгновенно.
          │
          └── НЕТ → Модель должна изменить стиль, формат или поведение?
                    Промпт слишком длинный и дорогой на масштабе?
                    │
                    ├── ДА → Fine-tuning.
                    │
                    └── НЕТ → Вернись к промпту.
                              Скорее всего, он недостаточно хорош.

Принцип: промпт → RAG → fine-tuning. Начинай с простого. Усложняй только когда простое не работает.

Этот принцип — из исследования Anthropic «Building Effective Agents». Он работает и для агентов (урок 5.2), и для адаптации моделей.

Источник: Anthropic — Building Effective Agents
https://www.anthropic.com/research/building-effective-agents

Когда fine-tuning нужен (и когда нет)

Нужен

1. Стабильный формат вывода
   Модель ВСЕГДА должна отвечать JSON с конкретными полями.
   Промпт работает в 90% — fine-tuning даёт 99%.

2. Специфический стиль
   500 примеров реальных ответов поддержки →
   модель воспроизводит тон, длину, структуру точно.

3. Экономия на масштабе
   10 000 запросов/день × 500 токенов инструкции = 5 000 000 токенов.
   С fine-tuning: инструкция не нужна → экономия 10x по токенам.

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

НЕ нужен

1. Нужны актуальные данные
   Fine-tuning — это снимок данных на момент обучения.
   Через неделю данные устарели → нужно переобучать.
   Используй RAG — данные обновляются мгновенно.

2. Нужны факты из конкретных документов
   "Какая цена у товара X?" — fine-tuning может запомнить неточно.
   RAG найдёт точный фрагмент из прайс-листа.

3. Мало данных (< 50 примеров)
   Fine-tuning требует минимум 50 примеров.
   Рекомендуется 200-500 для хорошего качества.
   Если данных мало — используй few-shot промпт (2-5 примеров в промпте).

4. Задача решается промптом
   Попробуй сначала. Добавь few-shot примеры (урок 2.4).
   Если промпт работает в 95%+ случаев — fine-tuning не нужен.

Как работает fine-tuning внутри

Не нужно знать математику. Достаточно понимать принцип — он простой.

Три шага

ШАГ 1: Подготовка данных
  Собираешь примеры: "вот вход → вот правильный выход"
  Формат: файл JSONL (каждая строка — один пример)
  Количество: 200-500 примеров для хорошего результата

ШАГ 2: Обучение
  Загружаешь файл в API провайдера (OpenAI, Google, и др.)
  Провайдер прогоняет модель через твои примеры
  Модель корректирует свои параметры на каждом примере
  Занимает 10-60 минут

ШАГ 3: Использование
  Получаешь имя новой модели (например, ft:gpt-4o-mini:personal::abc123)
  Используешь через тот же API, что и обычную модель
  Промпт теперь можно сократить — модель уже знает формат

Что происходит на шаге 2 подробнее

Модель проходит через все примеры несколько раз. Каждый полный проход называется эпоха (epoch).

Пример обучения:
  Вход:  "Товар пришёл сломанным, хочу вернуть деньги"
  Правильный выход: {"category": "return", "sentiment": "negative"}

Что делает модель на каждом примере:
  1. Получает вход
  2. Генерирует свой ответ: {"category": "question", "sentiment": "neutral"}
  3. Сравнивает с правильным ответом — видит, что ошиблась
  4. Чуть-чуть корректирует свои параметры, чтобы в следующий раз
     ответить ближе к правильному

         Ошибка уменьшается с каждой эпохой:

Эпоха 1: ████████████████████░░░░░  80% ошибок (модель ещё не выучила)
Эпоха 2: ██████████░░░░░░░░░░░░░░░  40% ошибок (начала понимать паттерн)
Эпоха 3: ████░░░░░░░░░░░░░░░░░░░░░  15% ошибок (почти выучила)
Эпоха 4: ██░░░░░░░░░░░░░░░░░░░░░░░   5% ошибок (готово)

Аналогия. Как ребёнок учит таблицу умножения. Первый раз — много ошибок. После 10 повторений — ошибается реже. После 50 — отвечает автоматически. Fine-tuning — это те же повторения, только для языковой модели.

Что такое JSONL

JSONL (JSON Lines) — формат файла, где каждая строка — отдельный JSON-объект. Это стандартный формат для обучающих данных.

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

Обычный JSON (один объект):
[
  {"input": "...", "output": "..."},
  {"input": "...", "output": "..."}
]

JSONL (каждая строка — отдельный объект):
{"input": "...", "output": "..."}
{"input": "...", "output": "..."}

Для OpenAI fine-tuning каждая строка — это диалог в формате messages (тот же формат, что и при обычном вызове API из урока 2.3):

{"messages": [{"role": "system", "content": "Classify the message."}, {"role": "user", "content": "Товар сломан"}, {"role": "assistant", "content": "{\"category\": \"return\"}"}]}
{"messages": [{"role": "system", "content": "Classify the message."}, {"role": "user", "content": "Спасибо!"}, {"role": "assistant", "content": "{\"category\": \"feedback\"}"}]}

Модель учится: «когда system и user говорят вот это — assistant должен ответить вот это».


Типы fine-tuning

Есть два основных подхода к дообучению. Различие — в том, какую часть модели мы меняем.

Full fine-tuning (полное дообучение)

Меняются все параметры модели. У GPT-4 — сотни миллиардов параметров. Менять каждый из них — как перекрашивать каждый кирпич в многоэтажном доме.

Модель:    ████████████████████████████████  (все параметры)
Меняем:    ████████████████████████████████  (все — каждый подстраивается)

Требования: мощные GPU (A100, H100 — стоят $10 000+)
Время:      часы — дни
Стоимость:  $100 — $10 000+
Кто делает: крупные компании с ML-командами

LoRA (Low-Rank Adaptation) — частичное дообучение

LoRA — метод, который не трогает основную модель. Вместо этого он добавляет маленький «адаптер» — дополнительный слой параметров, который учится новому навыку.

Это как надеть на модель «очки специалиста»: базовое зрение (базовая модель) остаётся, а очки (адаптер) добавляют фокус на конкретную область.

Модель:    ████████████████████████████████  (все параметры)
Меняем:    ██                                (только адаптер — 1-5%)

Требования: обычная GPU (RTX 3090/4090) или облако
Время:      минуты — часы
Стоимость:  $1 — $100
Кто делает: разработчики, стартапы, исследователи

QLoRA — ещё более экономичная версия LoRA. Буква Q означает Quantization (квантизация) — сжатие модели, чтобы она занимала меньше памяти. С QLoRA можно дообучить модель с 7 миллиардами параметров на GPU с 8 ГБ памяти — например, на обычной игровой видеокарте.

Сравнение

┌────────────────────────────────────────────────────────┐
│                                                        │
│  Full fine-tuning         LoRA / QLoRA                 │
│  ─────────────────        ──────────────               │
│  Меняет всю модель        Добавляет адаптер            │
│  Нужны мощные GPU         Хватает обычной GPU          │
│  Стоит $100-10 000        Стоит $1-100                 │
│  Часы — дни               Минуты — часы                │
│  Немного лучше качество   Почти такое же качество      │
│  Для компаний с ML-       Для всех остальных           │
│    командами                                           │
│                                                        │
│  Аналогия:                Аналогия:                    │
│  Перекрасить весь дом     Повесить новые шторы         │
│                                                        │
└────────────────────────────────────────────────────────┘

Для большинства задач LoRA достаточно. Full fine-tuning оправдан, только когда нужен максимально точный результат и есть ресурсы.

Источник: Hu et al. — "LoRA: Low-Rank Adaptation of Large Language Models" (2021)
https://arxiv.org/abs/2106.09685

Где можно дообучить модель

Вариант 1: Managed fine-tuning (через API провайдера)

Провайдер берёт на себя всё: GPU, обучение, хостинг. Ты загружаешь данные → получаешь модель.

Провайдер Что можно дообучить Метод Стоимость обучения
OpenAI GPT-4o, GPT-4o-mini Managed От $0.30 за 500 примеров
Google Gemini Managed Зависит от модели
Together AI Llama, Mistral, Qwen LoRA в облаке От $0.50

Anthropic (создатели Claude) пока не предлагают публичный API для fine-tuning. Поэтому для практики в этом уроке используется OpenAI — у них самый простой и доступный процесс.

Вариант 2: Самостоятельное дообучение (open-source модели)

Если данные нельзя отправлять на серверы OpenAI (конфиденциальность) или нужен полный контроль — можно дообучить open-source модель (Llama от Meta, Mistral, Qwen от Alibaba).

Что тебе нужно?
│
├── Быстро попробовать LoRA на бесплатном GPU
│   → Unsloth (Python-библиотека, оптимизирована для скорости)
│   → Google Colab с бесплатным GPU (T4)
│
├── Полноценный fine-tuning pipeline
│   → Hugging Face Transformers + PEFT
│   → Стандарт индустрии, много документации
│
├── Облачный fine-tuning без своего GPU
│   → Together AI, Modal, Anyscale
│   → Загружаешь данные → получаешь модель
│
└── Локально на своём Mac
    → MLX (фреймворк от Apple для Apple Silicon)
    → Ollama + LoRA-адаптеры

Для этого курса достаточно знать, что эти варианты существуют. На практике — OpenAI fine-tuning API покрывает большинство задач и не требует своих GPU.

Источники:
  OpenAI Fine-tuning: https://platform.openai.com/docs/guides/fine-tuning
  Unsloth: https://github.com/unslothai/unsloth
  Hugging Face PEFT: https://huggingface.co/docs/peft

Подготовка данных: как сделать правильно

Качество данных — самое важное в fine-tuning. Плохие данные → плохая модель. Никакое количество эпох не исправит ошибки в обучающих примерах.

Требования к данным

Количество:
  Минимум:      50 примеров (результат будет слабый)
  Хорошо:       200-500 примеров
  Отлично:      500-5000 примеров
  Максимум:     до 50 000 (OpenAI)

Качество важнее количества:
  100 чистых, правильных примеров > 1000 грязных с ошибками

Три главных ошибки в данных

Ошибка 1: Противоречивые примеры

"Товар сломан"  → category: "feedback"    ← НЕПРАВИЛЬНО
"Товар сломан"  → category: "return"      ← правильно

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

Ошибка 2: Несбалансированные категории

Данные:
  300 примеров → category: "return"
   50 примеров → category: "delivery"
   20 примеров → category: "payment"
   10 примеров → category: "feedback"

Результат: модель будет всё подряд классифицировать как "return",
потому что видела эту категорию в 79% примеров.

Решение: примерно одинаковое количество примеров на каждую категорию.

Ошибка 3: Ответы не такие, какие нужны

В данных: ассистент пишет по 500 слов с объяснениями
В реальности: нужен короткий JSON без объяснений

Модель выучит писать длинно — потому что так в примерах.

Правило: ответ в обучающих данных должен быть ТОЧНО таким,
какой ты хочешь получать от модели.

Практика: fine-tuning через OpenAI API

Что нужно

pip install openai python-dotenv

Нужен API-ключ OpenAI (не Anthropic — у Anthropic нет публичного fine-tuning).

Получить ключ: https://platform.openai.com/api-keys

В файле .env:

OPENAI_API_KEY=sk-...

Что строим

Классификатор обращений клиентов. На входе — сообщение клиента. На выходе — JSON с категорией, тональностью и приоритетом.

┌──────────────────────────────────────────────────────────┐
│                                                          │
│  Вход:  "Товар пришёл сломанным, хочу вернуть деньги"  │
│                     │                                    │
│                     ▼                                    │
│          ┌────────────────────┐                          │
│          │  Дообученная модель │                          │
│          │  (ft:gpt-4o-mini)  │                          │
│          └─────────┬──────────┘                          │
│                    │                                     │
│                    ▼                                     │
│  Выход: {"category": "return",                          │
│          "sentiment": "negative",                        │
│          "priority": "high"}                             │
│                                                          │
└──────────────────────────────────────────────────────────┘

Шаг 1: Создаём обучающие данные

# prepare_data.py — создаёт файл с обучающими данными для fine-tuning

import json

# === ОБУЧАЮЩИЕ ПРИМЕРЫ ===
# Каждый пример: текст обращения клиента → правильная классификация
# В реальном проекте таких примеров нужно 200-500
# Для демонстрации используем 25

training_examples = [

    # --- ВОЗВРАТ (return) ---

    {"input": "Товар пришёл сломанным, хочу вернуть деньги",
     "output": {"category": "return", "sentiment": "negative", "priority": "high"}},

    {"input": "Можно ли обменять на другой размер?",
     "output": {"category": "return", "sentiment": "neutral", "priority": "medium"}},

    {"input": "Хочу отменить заказ и получить возврат",
     "output": {"category": "return", "sentiment": "negative", "priority": "high"}},

    {"input": "Товар не соответствует описанию на сайте",
     "output": {"category": "return", "sentiment": "negative", "priority": "high"}},

    {"input": "Как оформить возврат? Не подошёл цвет",
     "output": {"category": "return", "sentiment": "neutral", "priority": "medium"}},

    # --- ДОСТАВКА (delivery) ---

    {"input": "Когда будет доставка заказа #4521?",
     "output": {"category": "delivery", "sentiment": "neutral", "priority": "medium"}},

    {"input": "Курьер не приехал в назначенное время",
     "output": {"category": "delivery", "sentiment": "negative", "priority": "high"}},

    {"input": "Можно ли изменить адрес доставки?",
     "output": {"category": "delivery", "sentiment": "neutral", "priority": "medium"}},

    {"input": "Заказ показывает статус 'в пути' уже неделю",
     "output": {"category": "delivery", "sentiment": "negative", "priority": "high"}},

    {"input": "Доставка пришла вовремя, всё отлично",
     "output": {"category": "delivery", "sentiment": "positive", "priority": "low"}},

    # --- ОПЛАТА (payment) ---

    {"input": "Деньги списались, но заказ не оформлен",
     "output": {"category": "payment", "sentiment": "negative", "priority": "high"}},

    {"input": "Какие способы оплаты доступны?",
     "output": {"category": "payment", "sentiment": "neutral", "priority": "low"}},

    {"input": "Не проходит оплата картой",
     "output": {"category": "payment", "sentiment": "negative", "priority": "high"}},

    {"input": "Можно ли оплатить при получении?",
     "output": {"category": "payment", "sentiment": "neutral", "priority": "low"}},

    {"input": "Пришёл двойной чек за один заказ",
     "output": {"category": "payment", "sentiment": "negative", "priority": "high"}},

    # --- ОТЗЫВ (feedback) ---

    {"input": "Отличный магазин, всё понравилось!",
     "output": {"category": "feedback", "sentiment": "positive", "priority": "low"}},

    {"input": "Качество товара превзошло ожидания",
     "output": {"category": "feedback", "sentiment": "positive", "priority": "low"}},

    {"input": "Обслуживание ужасное, больше не приду",
     "output": {"category": "feedback", "sentiment": "negative", "priority": "medium"}},

    {"input": "Нормально, но ничего особенного",
     "output": {"category": "feedback", "sentiment": "neutral", "priority": "low"}},

    {"input": "Рекомендую друзьям, очень довольна покупкой",
     "output": {"category": "feedback", "sentiment": "positive", "priority": "low"}},

    # --- ВОПРОС (question) ---

    {"input": "Есть ли у вас доставка в Казахстан?",
     "output": {"category": "question", "sentiment": "neutral", "priority": "low"}},

    {"input": "Какой срок гарантии на электронику?",
     "output": {"category": "question", "sentiment": "neutral", "priority": "low"}},

    {"input": "Работает ли магазин в праздники?",
     "output": {"category": "question", "sentiment": "neutral", "priority": "low"}},

    {"input": "Есть ли скидки для постоянных клиентов?",
     "output": {"category": "question", "sentiment": "neutral", "priority": "low"}},

    {"input": "Можно ли заказать товар оптом?",
     "output": {"category": "question", "sentiment": "neutral", "priority": "low"}},
]


# === КОНВЕРТАЦИЯ В ФОРМАТ OPENAI (JSONL) ===

# Системный промпт — одинаковый для всех примеров.
# Он короткий, потому что после fine-tuning модель
# уже "знает" формат и категории.
system_prompt = "Classify the customer message. Respond with JSON only."

# Открываем файл для записи
with open("training_data.jsonl", "w", encoding="utf-8") as f:
    for example in training_examples:

        # Каждая строка JSONL — диалог в формате messages
        # Тот же формат, что и при обычном вызове API (урок 2.3)
        line = {
            "messages": [
                {"role": "system", "content": system_prompt},
                {"role": "user", "content": example["input"]},
                {"role": "assistant", "content": json.dumps(example["output"])}
                # json.dumps превращает словарь Python в строку JSON
                # {"category": "return"} → '{"category": "return"}'
            ]
        }

        # Записываем одну строку в файл
        # ensure_ascii=False — чтобы кириллица сохранялась как есть
        f.write(json.dumps(line, ensure_ascii=False) + "\n")

print(f"Создано {len(training_examples)} примеров в training_data.jsonl")

После запуска появится файл training_data.jsonl. Каждая строка — один пример. Открой его в текстовом редакторе и убедись, что формат правильный: system → user → assistant.

Шаг 2: Загрузка данных и запуск fine-tuning

# fine_tune.py — загружает данные в OpenAI и запускает дообучение

import os
from dotenv import load_dotenv
from openai import OpenAI

load_dotenv()  # загружает OPENAI_API_KEY из .env

client = OpenAI()  # клиент OpenAI API (ключ берёт из переменной окружения)


# === ШАГ 1: ЗАГРУЗКА ФАЙЛА ===

# OpenAI нужно сначала загрузить файл на их сервер.
# purpose="fine-tune" говорит: "этот файл — для дообучения"
print("Загружаю файл с данными на сервер OpenAI...")

with open("training_data.jsonl", "rb") as f:  # "rb" = read binary (требование API)
    training_file = client.files.create(
        file=f,
        purpose="fine-tune"
    )

print(f"  Файл загружен. ID: {training_file.id}")
# Пример ID: file-abc123


# === ШАГ 2: ЗАПУСК FINE-TUNING ===

# Создаём задачу на дообучение.
# model — какую базовую модель дообучать.
# gpt-4o-mini — самая дешёвая модель OpenAI для fine-tuning.
print("\nЗапускаю fine-tuning...")

job = client.fine_tuning.jobs.create(
    training_file=training_file.id,
    model="gpt-4o-mini-2024-07-18",
    hyperparameters={
        "n_epochs": 3  # сколько раз пройти по всем примерам
        # 3 эпохи — хороший дефолт для большинства задач
        # Больше эпох = модель лучше запоминает, но может "переучиться"
    }
)

print(f"  Job создан. ID: {job.id}")
print(f"  Статус: {job.status}")
# Статус "validating_files" → "queued" → "running" → "succeeded"

print(f"\n  Дообучение займёт 10-30 минут.")
print(f"  Проверить статус:")
print(f"    python check_status.py {job.id}")

Шаг 3: Проверка статуса

# check_status.py — проверяет, готово ли дообучение

import sys
import os
from dotenv import load_dotenv
from openai import OpenAI

load_dotenv()
client = OpenAI()

# ID задачи — передаём аргументом при запуске:
# python check_status.py ftjob-abc123
job_id = sys.argv[1] if len(sys.argv) > 1 else "ftjob-xxxxxxxxxx"
# sys.argv — список аргументов командной строки
# sys.argv[0] = "check_status.py" (имя скрипта)
# sys.argv[1] = "ftjob-abc123" (первый аргумент — ID задачи)

# Запрашиваем статус задачи
job = client.fine_tuning.jobs.retrieve(job_id)

print(f"Статус: {job.status}")
# validating_files — проверяет формат данных
# queued — в очереди на обучение
# running — идёт обучение
# succeeded — готово!
# failed — ошибка

if job.status == "succeeded":
    print(f"\nГотово! Имя модели: {job.fine_tuned_model}")
    print(f"Используй это имя в параметре model= при вызове API.")
    # Пример имени: ft:gpt-4o-mini-2024-07-18:personal::abc123

elif job.status == "failed":
    print(f"\nОшибка: {job.error}")

else:
    print(f"\nЕщё в процессе. Подожди и проверь снова.")


# Показать последние события (что происходит внутри)
print("\nПоследние события:")
events = client.fine_tuning.jobs.list_events(
    fine_tuning_job_id=job_id,
    limit=5  # последние 5 событий
)
for event in events.data:
    print(f"  {event.message}")

Шаг 4: Используем дообученную модель

# use_model.py — тестируем дообученную модель

import os
from dotenv import load_dotenv
from openai import OpenAI

load_dotenv()
client = OpenAI()

# Имя дообученной модели — из check_status.py после "succeeded"
# Замени на своё имя модели:
FINE_TUNED_MODEL = "ft:gpt-4o-mini-2024-07-18:personal::abc123"


def classify(message):
    """
    Классифицирует обращение клиента.
    Использует дообученную модель вместо базовой.
    """
    response = client.chat.completions.create(
        model=FINE_TUNED_MODEL,  # ← наша дообученная модель
        messages=[
            # Промпт короткий — модель уже знает формат и категории
            {"role": "system", "content": "Classify the customer message. Respond with JSON only."},
            {"role": "user", "content": message}
        ],
        max_tokens=100  # ответ короткий — JSON в одну строку
    )
    return response.choices[0].message.content


# === ТЕСТИРУЕМ НА НОВЫХ ОБРАЩЕНИЯХ ===
# Эти сообщения НЕ были в обучающих данных.
# Модель должна обобщить паттерн и правильно классифицировать.

test_messages = [
    "Посылка потерялась где-то в пути",
    "Вы лучший магазин, буду заказывать ещё!",
    "Сколько стоит экспресс-доставка?",
    "Верните деньги немедленно, товар бракованный!",
    "Не могу войти в личный кабинет",
]

print("=== Тестирование дообученной модели ===\n")

for msg in test_messages:
    result = classify(msg)
    print(f"  Вход:  {msg}")
    print(f"  Выход: {result}")
    print()

Пример вывода

=== Тестирование дообученной модели ===

  Вход:  Посылка потерялась где-то в пути
  Выход: {"category": "delivery", "sentiment": "negative", "priority": "high"}

  Вход:  Вы лучший магазин, буду заказывать ещё!
  Выход: {"category": "feedback", "sentiment": "positive", "priority": "low"}

  Вход:  Сколько стоит экспресс-доставка?
  Выход: {"category": "question", "sentiment": "neutral", "priority": "low"}

  Вход:  Верните деньги немедленно, товар бракованный!
  Выход: {"category": "return", "sentiment": "negative", "priority": "high"}

  Вход:  Не могу войти в личный кабинет
  Выход: {"category": "question", "sentiment": "negative", "priority": "medium"}

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


Разбор: что делает каждая часть

Файл данных (prepare_data.py)

training_examples = [
    {"input": "Товар сломан", "output": {"category": "return", ...}},
    ...
]

Список словарей Python. Каждый словарь — один пример: input (что говорит клиент) и output (правильный ответ). Это человекочитаемый формат — удобно редактировать и добавлять примеры.

json.dumps(example["output"])

json.dumps() превращает словарь Python в строку JSON. Это нужно, потому что в поле content API ожидает строку, а не словарь.

f.write(json.dumps(line, ensure_ascii=False) + "\n")

Записывает одну строку в файл и добавляет перенос строки (\n). Так получается формат JSONL — одна строка = один пример.

Запуск дообучения (fine_tune.py)

client.files.create(file=f, purpose="fine-tune")

Загружает файл на серверы OpenAI. purpose="fine-tune" говорит серверу, что файл предназначен для дообучения, а не для другой задачи.

client.fine_tuning.jobs.create(
    training_file=training_file.id,
    model="gpt-4o-mini-2024-07-18",
    hyperparameters={"n_epochs": 3}
)

Создаёт задачу на дообучение. Три параметра: - training_file — ID загруженного файла - model — какую базовую модель дообучать - n_epochs — сколько раз пройти по всем примерам (3 — хороший дефолт)

Использование модели (use_model.py)

client.chat.completions.create(
    model=FINE_TUNED_MODEL,  # ft:gpt-4o-mini:personal::abc123
    messages=[...]
)

Точно тот же API, что и для обычной модели. Единственное отличие — в model указывается имя дообученной модели (начинается с ft:). Всё остальное — идентично.


Сколько стоит fine-tuning

Стоимость обучения

OpenAI считает стоимость по количеству токенов в обучающих данных:

Формула:
  токены в данных × цена за токен × количество эпох

Пример с gpt-4o-mini:
  25 примеров × ~100 токенов = 2 500 токенов
  2 500 × $0.003 (за 1K) × 3 эпохи = ~$0.02

  500 примеров × ~100 токенов = 50 000 токенов
  50 000 × $0.003 × 3 = ~$0.45

Для gpt-4o (более мощная модель):
  500 примеров × ~100 токенов = 50 000 токенов
  50 000 × $0.025 × 3 = ~$3.75

Стоимость использования (inference)

Дообученная модель чуть дороже базовой:

gpt-4o-mini (базовая):      $0.15 / 1M input   $0.60 / 1M output
gpt-4o-mini (fine-tuned):   $0.30 / 1M input   $1.20 / 1M output
                             (2x дороже за токен)

НО промпты после fine-tuning короче (не нужны инструкции),
поэтому реальная стоимость за запрос может быть НИЖЕ.

Где главная стоимость

┌──────────────────────────────────────────────────────┐
│                                                      │
│  Обучение модели:     $0.50 — $5.00                 │
│  Использование:       зависит от объёма              │
│                                                      │
│  Главная стоимость:   ВРЕМЯ НА ПОДГОТОВКУ ДАННЫХ    │
│                                                      │
│  Собрать 500 примеров,                              │
│  проверить каждый на правильность,                  │
│  сбалансировать категории,                          │
│  привести к единому формату —                       │
│  это часы или дни работы человека.                   │
│                                                      │
└──────────────────────────────────────────────────────┘
Источник: OpenAI — Pricing
https://openai.com/api/pricing/

Переобучение (overfitting): главная ловушка

Overfitting (переобучение) — ситуация, когда модель слишком хорошо запомнила обучающие примеры, но не может обобщить на новые данные.

Как это выглядит

На обучающих данных:
  "Товар пришёл сломанным" → {"category": "return"} ← правильно

На новых данных:
  "Заказ повреждён при доставке" → {"category": "delivery"} ← ОШИБКА
  (правильно: "return" — это тоже возврат, но модель не обобщила)

Аналогия

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

Признаки переобучения

✓ На обучающих примерах — идеальные результаты
✗ На новых примерах — ошибки
✗ Модель повторяет фразы из обучающих данных дословно
✗ Модель не справляется с формулировками, которых не было в данных

Как избежать

1. Больше данных (200-500, не 25)
   Чем больше разнообразных примеров — тем лучше обобщение.

2. Меньше эпох (2-4, не 10)
   Слишком много эпох = модель начинает зубрить.

3. Validation data (проверочные данные)
   Отложить 20% примеров и не использовать их для обучения.
   Проверять качество на них — если на проверочных ошибки растут,
   а на обучающих падают — это переобучение.

4. Разнообразие формулировок
   Разные способы сказать одно и то же для каждой категории.
   "Товар сломан", "Пришёл бракованный", "Не работает" —
   всё это "return", но сказано по-разному.

Fine-tuning в реальном мире

┌──────────────────────────────────────────────────────────┐
│           КТО И КАК ИСПОЛЬЗУЕТ FINE-TUNING                │
│                                                           │
│  Служба поддержки                                        │
│    Модель классифицирует обращения по категориям          │
│    и приоритетам. 100 000 обращений/день — промпт         │
│    был бы слишком дорогим. Fine-tuning экономит 10x.      │
│                                                           │
│  Юридические компании                                    │
│    Модель генерирует документы в точном формате:          │
│    структура параграфов, юридические термины, ссылки      │
│    на статьи закона. Промпт не даёт такой стабильности.   │
│                                                           │
│  Медицина                                                │
│    Модель заполняет клинические записи в формате          │
│    МКБ-10. Специфическая терминология + строгий формат    │
│    = идеальная задача для fine-tuning.                     │
│                                                           │
│  E-commerce                                              │
│    Автоматическая категоризация товаров.                  │
│    "Синяя хлопковая футболка XL" →                        │
│    {category: "одежда/футболки", size: "XL",              │
│     material: "хлопок", color: "синий"}                    │
│                                                           │
│  Контент и маркетинг                                     │
│    Модель пишет посты, письма, описания в фирменном       │
│    стиле компании. 500 примеров текстов компании →        │
│    модель воспроизводит тон и структуру.                  │
│                                                           │
└──────────────────────────────────────────────────────────┘

Практика

Задание 1: Подготовь данные

Запусти prepare_data.py. Открой файл training_data.jsonl в текстовом редакторе. Найди в каждой строке три роли: system, user, assistant. Добавь 5 своих примеров в training_examples (придумай обращения клиентов) и пересоздай файл.

Задание 2: Запусти fine-tuning (опционально)

Если есть API-ключ OpenAI — запусти полный цикл: загрузка данных → обучение → проверка статуса → тестирование. Стоимость для 25 примеров на gpt-4o-mini — меньше $0.05. Протестируй модель на обращениях, которых не было в данных.

Задание 3: Сравни с промптом

Отправь те же тестовые обращения в обычную (не дообученную) модель с подробным промптом: - Опиши категории, формат JSON, примеры - Сравни: стабильность формата, длину промпта, качество классификации - Подсчитай разницу в токенах (длинный промпт vs. короткий после fine-tuning)

Это поможет на практике увидеть, когда fine-tuning даёт преимущество, а когда достаточно хорошего промпта.


Задачки на закрепление

Задача 1: Что такое fine-tuning простыми словами?

Ответ Fine-tuning — дополнительное обучение готовой модели на своих примерах. Модель уже умеет понимать язык и рассуждать — fine-tuning добавляет конкретный навык: стиль, формат, терминологию. Как опытный врач, который прошёл стажировку в скорой помощи — базовые знания остались, добавилась специализация. После дообучения модель работает через тот же API, но с коротким промптом — она уже «знает», что от неё требуется.

Задача 2: Когда лучше RAG, а когда fine-tuning?

Ответ RAG — когда модели нужны конкретные факты: документы, цены, правила, актуальные данные. Факты хранятся в базе и обновляются мгновенно. Fine-tuning — когда модель должна изменить поведение: стиль, формат, терминологию. Навык «вшивается» в модель при обучении. Пример: «ответить по документации компании» → RAG. «Всегда отвечать в формате JSON с полями category и priority» → fine-tuning.

Задача 3: Что такое LoRA и зачем она нужна?

Ответ LoRA (Low-Rank Adaptation) — метод частичного дообучения. Вместо того чтобы менять все параметры модели (миллиарды), LoRA добавляет маленький «адаптер» — 1-5% дополнительных параметров. Базовая модель не меняется, адаптер учится новому навыку. Это в десятки раз дешевле и быстрее полного fine-tuning, а качество — почти такое же. Аналогия: не перекрашивать весь дом, а повесить новые шторы.

Задача 4: Почему 100 чистых примеров лучше, чем 1000 грязных?

Ответ Модель учится воспроизводить паттерны из данных — какие бы они ни были. Если в данных противоречия (одинаковый вход → разный выход), модель получает конфликтующие сигналы и не может выучить стабильный паттерн. Если в данных ошибки (неправильные метки), модель выучит ошибки. 100 правильных, непротиворечивых примеров дают чёткий сигнал. 1000 грязных — шум, который модель не сможет превратить в надёжное поведение.

Задача 5: Что такое overfitting?

Ответ Overfitting (переобучение) — когда модель слишком хорошо запомнила обучающие примеры, но не может обобщить на новые. Как студент, который зазубрил ответы на конкретные вопросы — на экзамене с другой формулировкой он теряется. Признаки: идеальные результаты на обучающих данных, ошибки на новых. Решение: больше данных, меньше эпох, разнообразные формулировки, проверочные данные (validation).

Глоссарий

Термин Что значит
Fine-tuning Дообучение готовой модели на своих примерах — модель получает новый навык (стиль, формат, терминологию)
JSONL JSON Lines — формат файла, где каждая строка — отдельный JSON-объект. Стандарт для обучающих данных
Эпоха (epoch) Один полный проход модели по всем обучающим примерам. 3 эпохи = модель прошла по данным 3 раза
LoRA Low-Rank Adaptation — метод частичного дообучения. Добавляет маленький адаптер (~1-5% параметров) вместо изменения всей модели
QLoRA LoRA + квантизация (сжатие модели). Позволяет дообучать на обычной GPU с 8 ГБ памяти
Full fine-tuning Полное дообучение — меняются все параметры модели. Дорого, но максимально точно
Адаптер Маленький набор дополнительных параметров при LoRA. Как «очки специалиста» для модели
Гиперпараметры Настройки процесса обучения: количество эпох, learning rate, batch size
Training data Обучающие данные — примеры «вход → правильный выход», на которых модель учится
Validation data Проверочные данные — модель на них НЕ учится, но по ним измеряется качество
Overfitting Переобучение — модель зазубрила примеры, но не обобщает на новые данные
Base model Базовая модель до дообучения (GPT-4o-mini, Llama, Mistral)
Managed fine-tuning Дообучение через API провайдера (OpenAI, Google) — без своих GPU
Open-source модель Модель с открытым кодом (Llama, Mistral, Qwen) — можно скачать и дообучить самостоятельно
Inference Использование обученной модели для генерации ответов (в отличие от training — обучения)
Unsloth Python-библиотека для быстрого LoRA fine-tuning open-source моделей

Главное

Fine-tuning = дообучение готовой модели на своих примерах
  Модель не учится с нуля — она получает специализацию.

Когда нужен:
  • Стабильный формат вывода (JSON, структура)
  • Специфический стиль или тон
  • Экономия токенов на масштабе (короче промпты)
  • Специализированная терминология

Когда НЕ нужен:
  • Нужны актуальные данные → RAG
  • Задача решается промптом → промпт-инженерия
  • Мало данных (< 50 примеров) → few-shot в промпте

Порядок: промпт → RAG → fine-tuning
  Начинай с простого. Усложняй только когда простое не работает.

Как работает:
  1. Собрать 200-500 примеров "вход → правильный выход" (JSONL)
  2. Загрузить в OpenAI → запустить обучение (10-30 минут)
  3. Получить модель → использовать через тот же API

Типы:
  Full fine-tuning — дорого, все параметры, для больших компаний
  LoRA — дёшево, маленький адаптер, для всех остальных

Стоимость:
  Обучение: $0.02-5.00 (зависит от данных и модели)
  Главная цена: время на подготовку качественных данных

Ловушка — overfitting:
  Модель зазубрила примеры, но не обобщает на новые.
  Решение: больше данных, меньше эпох, разнообразие, validation.

Что дальше?

Fine-tuning делает модель точнее для конкретных задач. Но AI-продукт — это не только точность. Это ещё и стоимость, скорость и масштаб. Как выбрать правильную модель для задачи? Как кешировать ответы? Как сократить промпты без потери качества? В следующем уроке — оптимизация и стоимость: экономика AI-продуктов.

← ПРЕДЫДУЩИЙ СЛЕДУЮЩИЙ →