Модуль 5. Датасет — 80% результата

Цель модуля: собрать пары «запрос → эталонный ответ» в правильном формате (JSONL, chat messages, классификатор), почистить их и разделить на train/val без утечки. После модуля вы знаете, сколько примеров нужно под вашу задачу и почему 300 чистых пар побеждают 3000 грязных.

Перед началом: модуль 4 — вы уже решили, что задача решается fine-tuning. Инструменты: песочница сайта (сниппет модуля), python3 локально, таблица или CSV для своих пар. Трудоёмкость: ~2 ч (теория 50 мин · практика 60 мин · квиз 10 мин).

🔥 Два тюнинга: 300 чистых против 3000 грязных

Команда А и команда Б дообучали одну и ту же базовую модель для поддержки. А выгрузила 3000 пар из сырых логов: дубли, противоречия («вернуть нельзя» и «вернём за три дня» на одинаковый вопрос), опечатки, перекос — 70% пар про оплату. Б вручную собрала 300: прошлась по логам, отобрала лучшие ответы, вычистила дубли, свела противоречия, сбалансировала темы. На честной проверке (golden set, м8) модель команды Б выиграла по всем вопросам, кроме редких вариантов про оплату — той темы, которую А перекосила. Модель А отвечала про оплату увереннее — и чаще невпопад.

Мораль не «данных мало не бывает». Мораль: модель учит частоты. Дубль — это «этот ответ важнее». Противоречие — «отвечай случайно». Перекос — «большинство вопросов про оплату». Грязный датасет — программа, которую вы написали случайно. Этот модуль — про то, как писать её осознанно.

📖 5.1. Форматы: JSONL и его разновидности

Пары для генерации в AI Studio лежат в JSONL — по одному JSON-объекту на строку, три поля: instruction (постоянная системная инструкция), request (запрос пользователя), response (эталонный ответ):

jsonl
{"instruction": "Ты — оператор поддержки учебной платформы. Отвечай кратко.",
 "request": "Как вернуть деньги за курс?",
 "response": "Откройте страницу заказа и нажмите «Вернуть»."}
{"instruction": "Ты — оператор поддержки учебной платформы. Отвечай кратко.",
 "request": "Куда пропал платёж?",
 "response": "Платёж идёт до 15 минут; обновите страницу заказов."}

(Для читаемости объекты перенесены на несколько строк; в настоящем файле каждый объект занимает ровно одну строку — это и есть формат JSONL.) instruction обычно один и тот же во всём файле: так в дообучение «зашивается» роль (тюнинг YandexGPT Lite в м10 ест именно этот формат). Альтернатива — chat messages, OpenAI-совместимый формат с ролями, как в chat template из м3:

jsonl
{"messages": [{"role": "system", "content": "Ты — оператор поддержки."},
 {"role": "user", "content": "Как вернуть деньги за курс?"},
 {"role": "assistant", "content": "Откройте заказ и нажмите «Вернуть»."}]}

Для классификатора (разметка тикетов, маршрутизация) формат другой — текст и метки из фиксированного набора, без генерации ответа:

jsonl
{"text": "Не пришло подтверждение оплаты", "labels": ["оплата"]}
{"text": "Книга пришла повреждённой", "labels": ["доставка", "возврат"]}

Выбор формата — это выбор задачи: генерация (длинный response), диалог (messages с историей) или разметка (labels). Смешивать задачи в одном файле нельзя — валидация загрузки упадёт ещё до обучения (м10).

📖 5.2. Источники пар: где брать эталоны

  • Логи поддержки. Реальные вопросы пользователей и лучшие ответы операторов. Плюс: живой язык, реальные боли. Минус: ответы разного качества — отбор делает человек, а PII (имена, телефоны, номера карт) маскируется ещё до попадания в датасет.
  • Экспертные ответы. FAQ, регламенты, внутренние инструкции. Плюс: канонический ответ. Минус: язык эксперта — не язык пользователя; вопрос к экспертному тексту часто дописывают вручную.
  • Синтетика. Модель генерирует пары по вашим документам, человек ревьюит и правит. Плюс: быстро масштабируется, закрывает редкие темы. Минус: шаблонность и галлюцинации модели-генератора — без ревью вы получите «стиль модели о вашей базе», а не вашу базу.
  • Журнал вызовов работающей LLM-фичи. Если у сервиса уже есть LLM-эндпоинт, вы (надеемся) пишете журнал: промпт, ответ, модель, токены, исход — как в модуле 12. Такой журнал — готовый черновик датасета в реальном распределении: видно, с чем приходят чаще всего. Плюс: настоящие запросы, а не выдуманные. Минус: ответы модели — не эталоны; пары для обучения получаются только после разметки куратором. Это и есть тот самый вход петли из диаграммы цикла в модуле 12.

Рабочая пропорция для старта: ядро из реальных кейсов (60–80%) плюс экспертные и синтетические пары для тем, которых в логах мало.

📖 5.3. Гигиена: чистим до сплита, а не после

Порядок пайплайна важнее отдельных приёмов: сначала нормализация, дедупликация и балансировка, и только потом перемешивание и разбиение train/val. Нарушите порядок — копия val-пары окажется в train, и метрика будет врать. Это называется утечкой валидации, и врёт она тихо: loss красивый, модель хуже.

Запустите сниппет модуля в песочнице — он прогоняет 13 сырых пар (три темы: оплата, возврат, доставка) через весь пайплайн и печатает воронку:

text
Исходно: 13 пар
После точной дедупликации: 12 (убрано 1)
  near-dup (0.40): «Почему мне отказали в возврате?» ≈ «Почему отказали в возврате?»
  near-dup (1.00): «Когда придёт  книга» ≈ «Когда придёт книга?»
После near-dup: 10

Split: train=7, val=3
  train: возврат:1, доставка:3, оплата:3
  val: возврат:1, доставка:1, оплата:1

Шаг 1 — нормализация: strip().lower(), схлопывание повторных пробелов и пробела перед знаком препинания — «курс ?» и «курс?» обязаны стать одним ключом. Шаг 2 — точная дедупликация по ключу «request + response» убрала одну пару: «Как вернуть деньги за курс?» и «как вернуть деньги за курс ?» совпали после приведения регистра и чистки пробелов. Урок: решите до чистки, что считать «одинаковым» (регистр, пробелы, пунктуацию), и приведите всё сразу — иначе дубликаты просачиваются.

Шаг 3 — near-dup: шинглы (пары соседних слов, пунктуация вырезана) и коэффициент Жаккара. Порог 0.4 — «перефразированный дубль»; он подбирается под домен: 0.7 ловит почти копии, ниже — агрессивнее. Здесь пойманы две пары. «Почему мне отказали в возврате?» ≈ «Почему отказали в возврате?» (0.40) — перефразировка одного вопроса с тем же ответом. «Когда придёт книга» ≈ «Когда придёт книга?» (1.00) — копия, пережившая точную дедупликацию: в ключе вопросительный знак сохраняется, а шинглы пунктуацию вырезают, и вопросы совпали до последнего слова. Две ступени не избыточны: каждая ловит своё.

Шаг 4 — перемешивание с фиксированным seed (42): сплит обязан быть воспроизводимым, иначе завтра вы не сможете повторить эксперимент. Шаг 5 — стратифицированный split: ~25% каждой темы в val, но минимум одна пара на тему — итого train=7, val=3, и в val есть все три темы. Без стратификации случайный сплит маленького файла мог оставить в val, скажем, только «доставку» — и деградацию по «оплате» вы бы не увидели в принципе.

📖 5.4. Сколько нужно примеров

ЗадачаПорядок примеровКомментарий
Стиль и формат ответадесятки–сотни (50–300)нужен тон, а не факты
Доменный тон и жаргонсотни–тысячичем шире словарь домена, тем больше пар
Классификаторот пары десятков на классклассы должны быть сбалансированы

Это ориентиры, не гарантии. Качество важнее количества (крючок модуля), а редкие темы добираются точечно, а не «ещё десятью тысячами всего». Стартуйте с 50–200 пар, смотрите по ошибкам на val и golden set (м8), каких тем не хватает, — и добавляйте именно их.

📖 5.5. Компромиссы

  • Много vs чисто: грязные пары учат шуму и случайности, чистые дороги ревью. Правило: не добавляйте пару, которую не готовы защитить как эталон.
  • Реальные vs синтетические: реальные отражают живые формулировки, синтетика закрывает редкие темы, но тянет шаблонность. Ревью человека — обязательный фильтр для обоих.
  • Высокий vs низкий порог near-dup: высокий (0.7) ловит почти копии и сохраняет перефразировки — модель учит вариативность формулировок; низкий (0.4) убирает и перефразированных двойников, экономя объём ревью. Для классификатора дубли опаснее — порог ниже.
  • Val побольше vs train побольше: маленький val зашумлён, но каждый пример на счету для обучения. Рабочий минимум val — по 5–10 пар на тему или класс.

💻 Практика

  1. (10 мин) Запустите сниппет в песочнице. Сопоставьте каждую строку вывода с шагом пайплайна: нормализация → точная дедупликация → near-dup → shuffle с seed → стратифицированный split.
  2. (10 мин) Поднимите порог near-dup с 0.4 до 0.7 (строка if score >= 0.4:) и перезапустите. Перефразировка «Почему мне отказали в возврате?» с Жаккаром 0.40 перестанет ловиться: останется только копия с 1.00, из 13 пар — 11, split train=8/val=3. Решите, что для вашего домена хуже: лишний двойник в train или потерянная перефразировка.
  3. (25 мин) Соберите 20–50 пар своего домена (задачу вы выбрали в м4): вопрос из реального лога или FAQ плюс эталонный ответ. Формат — JSONL из 5.1. PII маскируйте сразу при сборке.
  4. (10 мин) Прогоните свои пары через гигиену локально: скопируйте сниппет, замените список RAW на свой. Посмотрите, сколько ушло дубликатов и есть ли тема, которой не оказалось в val.
  5. (5 мин) Ответьте письменно: сколько пар нужно вашей задаче по таблице 5.4 и каких тем вам сейчас не хватает?

🧩 Quiz "Проверь себя"

1. Почему сплит делают строго после дедупликации?
2. Чем опасен дубликат в датасете?
3. Сколько примеров минимум берут на класс для классификатора?
4. Что даёт стратифицированный split?
5. Почему «Когда придёт книга» / «Когда придёт книга?» поймал только near-dup?

✅ Итоги модуля

  • Назвать три формата (JSONL instruction/request/response, chat messages, классификатор) и задачи, для которых они нужны.
  • Перечислить источники пар и главный риск каждого.
  • Объяснить порядок «чистка → сплит» и что такое утечка валидации.
  • Разобрать воронку сниппета: 13 → 12 → 10 и роль порога near-dup (0.4 подбирается под домен).
  • Назвать порядок числа примеров для стиля, домена и классификатора.
  • Собрать 20–50 своих пар и прогнать их через гигиену.

🔗 Мостик к Модулю 6

Датасет готов: пары чистые, val не течёт, объём посчитан. Остался вопрос механики: что именно происходит с весами, когда вы нажимаете «обучить», и почему «просто дообучить все 7 миллиардов параметров» не выйдет даже на серьёзной GPU. Следующий модуль — «Полное дообучение, LoRA и QLoRA: что происходит с весами»: бюджет видеопамяти, низкоранговые дельты и адаптеры размером в мегабайты.