Процесс Supervised Fine-Tuning (SFT) через API

55 минут Урок 22

Введение: Когда промпт-инжиниринга уже недостаточно

Добро пожаловать в пятый модуль курса. До этого момента мы работали с Gemini как с невероятно умным, но «общим» инструментом. Мы использовали контекстное обучение (in-context learning), передавая примеры прямо в промпт. Это отлично работает для прототипов и простых задач.

Но что, если вам нужно, чтобы модель освоила уникальный корпоративный tone-of-voice, научилась генерировать код на редком проприетарном языке или строго следовала сложной медицинской номенклатуре? Длинные системные промпты увеличивают задержку (latency) и стоимость токенов, а модель всё равно иногда «галлюцинирует» или сбивается.

Здесь на сцену выходит Supervised Fine-Tuning (SFT). В контексте Gemini 3 API это процесс дообучения модели на вашем размеченном наборе данных. Мы не просто даем инструкции — мы корректируем веса модели (обычно через адаптеры, такие как LoRA), чтобы она интуитивно понимала задачу.

В этом уроке мы пройдем полный цикл: от подготовки «чистых» данных до запуска задачи тюнинга через Python SDK и использования полученной модели.

Этап 1: Подготовка данных — фундамент успеха

Золотое правило машинного обучения: Garbage In, Garbage Out. Для Gemini 3 качество данных важнее их количества. Часто 100 идеально размеченных примеров работают лучше, чем 5000 «грязных».

API ожидает данные в формате JSONL (JSON Lines), где каждая строка — это отдельный объект JSON, представляющий один диалог или задачу. Структура должна отражать то, как модель будет использоваться в продакшене.

Анатомия обучающего примера

Каждый объект должен содержать список сообщений (messages). Ключевые роли:

  • system (опционально): Задает контекст и поведение. В тюнинге это помогает закрепить «личность» модели.
  • user: Входные данные или вопрос пользователя.
  • model: Идеальный, эталонный ответ, к которому мы стремимся.

json
// Пример одной строки в файле dataset.jsonl
{
  "messages": [
    {
      "role": "system",
      "content": "Ты — помощник технической поддержки API, который отвечает кратко, использует JSON для примеров и избегает лишней вежливости."
    },
    {
      "role": "user",
      "content": "Как мне авторизоваться в сервисе?"
    },
    {
      "role": "model",
      "content": "Используйте заголовок Authorization с Bearer токеном. Пример:\n```json\n{\"Authorization\": \"Bearer <TOKEN>\"}\n```"
    }
  ]
}

Этап 2: Валидация и загрузка данных

Перед тем как тратить ресурсы на обучение, необходимо убедиться, что данные валидны. Частые ошибки: пустые поля content, нарушение чередования ролей (например, два user подряд без ответа модели) или превышение лимита токенов на один пример.

Для работы с Gemini 3 API мы будем использовать официальную библиотеку google-generativeai. Процесс начинается с загрузки файла в хранилище Google.

python
import google.generativeai as genai
import os

# Настройка API ключа
genai.configure(api_key=os.environ["GEMINI_API_KEY"])

# Путь к вашему файлу данных
file_path = "tech_support_dataset.jsonl"

# 1. Загрузка файла в File API
print(f"Загружаем файл {file_path}...")
training_file = genai.upload_file(
    path=file_path,
    display_name="Tech Support Dataset v1",
    mime_type="application/json"
)

print(f"Файл загружен. URI: {training_file.uri}")
print(f"Состояние: {training_file.state.name}")

Этап 3: Создание задачи тюнинга (Tuning Job)

Теперь, когда файл в облаке, мы можем запустить процесс обучения. В Gemini 3 API это асинхронная операция. Вы создаете задачу (Job), и она выполняется на серверах Google.

Ключевые гиперпараметры:

  • epoch_count (Количество эпох): Сколько раз модель увидит весь ваш датасет. Для малых датасетов (менее 500 примеров) рекомендуется 3-5 и более эпох. Для больших — 1-2. Слишком много эпох приведет к оверфиттингу (модель просто выучит примеры наизусть и потеряет гибкость).
  • batch_size: Обычно управляется автоматически, но определяет, сколько примеров обрабатывается за один шаг обновления весов.
  • learning_rate: Скорость обучения. В API часто используется множитель (multiplier), позволяющий корректировать базовую скорость.

Ниже приведен код для запуска задачи тюнинга для модели gemini-3.0-flash-tuning (базовая модель, оптимизированная для дообучения).

python
# 2. Создание задачи тюнинга

model_id = "models/gemini-3.0-flash-001" # Базовая модель

tuning_job = genai.create_tuned_model(
    # Уникальный идентификатор вашей новой модели
    tuned_model_id="tech-support-bot-v1",
    source_model=model_id,
    training_data=training_file,
    # Гиперпараметры
    epoch_count=5,
    batch_size=4,
    learning_rate=0.001,
    # Описание для удобства управления
    description="Модель для техподдержки с краткими JSON ответами"
)

print(f"Задача создана: {tuning_job.name}")
print("Ожидание завершения обучения... Это может занять от 10 минут до часа.")
Упражнение

Представьте, что вы готовите датасет для бота-юриста. У вас есть сырой диалог. Вам нужно преобразовать его в валидный JSON-объект для обучения. <br><br>Сырой диалог:<br>Клиент: Что мне грозит за проезд на красный?<br>Юрист: Согласно ст. 12.12 КоАП РФ, это штраф 1000 рублей. При повторном нарушении — 5000 рублей или лишение прав на 4-6 месяцев.<br><br>Задание: Напишите JSON-объект с system prompt 'Ты опытный юрист по административному праву РФ'.

Этап 4: Мониторинг и оценка результатов

Процесс обучения — это «черный ящик» для наблюдателя, но API предоставляет метрики. Самая важная из них — Loss (Функция потерь). В процессе обучения значение Loss должно падать.

Вам не нужно сидеть и смотреть на консоль, но скрипт должен уметь ждать завершения. Также важно проверять статус операции (metadata), чтобы увидеть ошибки, если датасет оказался битым.

python
import time

# 3. Полллинг статуса (ожидание завершения)

# Получаем объект модели (сначала это просто ссылка на задачу)
model = genai.get_tuned_model(f"tunedModels/tech-support-bot-v1")

while model.state.name == "CREATING":
    print(".", end="", flush=True)
    time.sleep(10)
    model = genai.get_tuned_model(f"tunedModels/tech-support-bot-v1")

if model.state.name == "ACTIVE":
    print("\nОбучение успешно завершено!")
    # Можно вывести кривую обучения (Loss), если API вернул метрики
    # print(model.tuning_task.snapshots)
elif model.state.name == "FAILED":
    print(f"\nОшибка обучения: {model.state.details}")
else:
    print(f"\nНеизвестный статус: {model.state.name}")

Этап 5: Инференс (Использование дообученной модели)

Как только статус модели перешел в ACTIVE, она готова к работе. Важно понимать: вы не скачиваете веса модели. Она хостится в инфраструктуре Gemini, и вы обращаетесь к ней по её уникальному имени.

Использование практически идентично вызову стандартной модели, меняется только model_name. Однако, теперь модель будет неявно применять те паттерны поведения, которые вы заложили в датасет. Системную инструкцию при инференсе можно не передавать, если она была жестко «зашита» в данные обучения, но для надежности её часто дублируют.

python
# 4. Использование новой модели

if model.state.name == "ACTIVE":
    tuned_model = genai.GenerativeModel(model_name="tunedModels/tech-support-bot-v1")

    response = tuned_model.generate_content(
        "Клиент не может найти кнопку выхода в интерфейсе."
    )

    print("Ответ модели:")
    print(response.text)
    # Ожидаемый ответ в стиле нашего датасета (кратко, возможно с JSON примером)
else:
    print("Модель не готова к использованию.")

Лучшие практики и частые ошибки

Опыт показывает, что 80% проблем с файн-тюнингом связаны не с кодом, а с методологией:

  • Проблема «Катастрофического забывания» (Catastrophic Forgetting): Если вы учите модель писать код на новом языке, она может «забыть», как писать стихи или поддерживать светскую беседу. SFT сужает специализацию. Если вам нужна универсальная модель, включайте в датасет разнообразные примеры, а не только узкоспециализированные.
  • Смешивание форматов: Не смешивайте в одном файле примеры, где бот ведет себя как «злой полицейский», и примеры с «добрым помощником». Это запутает модель.
  • Форматирование: Если в обучающих данных вы всегда используете Markdown для заголовков, а в промпте при использовании просите простой текст — возникнет конфликт. Будьте последовательны.

Когда НЕ стоит использовать SFT

Не используйте файн-тюнинг для добавления фактических знаний (например, "Какая выручка была у компании Х в 2024 году?"). Модели плохо запоминают факты через веса, и часто галлюцинируют. Для фактов используйте RAG (Retrieval-Augmented Generation). SFT нужен для изменения формы, стиля и логики рассуждений.

Вопрос

Вы обучили модель с 10 эпохами на маленьком датасете (50 примеров). При тестировании модель выдает ответы, слово в слово повторяющие примеры из обучения, но на новые, слегка измененные вопросы, отвечает бессвязно. Как называется эта проблема и как её решить?