Как вы подготовите датасет для fine-tuning, если у вас только неструктурированные диалоги с клиентами?

Краткий тезис

Подготовка датасета из неструктурированных диалогов — это многоэтапный процесс: от парсинга сырых логов до форматирования в инструкционные пары. Ключевые шаги: очистка от PII, фильтрация шума, балансировка, аугментация для разнообразия и автоматическая оценка качества через LLM-as-a-judge. Цель — получить чистый, сбалансированный датасет в формате, пригодном для fine-tuning (например, ChatML), с контролем утечек и смещений.

1. Понимание задачи: что такое неструктурированные диалоги

Неструктурированные диалоги — это сырые записи общения между оператором поддержки и клиентом, часто в виде текстовых логов без явных меток ролей, с временными метками, опечатками, вставками системных сообщений (например, «Перевод звонка…»). Fine-tuning на таких данных требует преобразования их в инструкционные пары (instruction‑response), чтобы модель научилась отвечать как оператор.

Основные риски: утечка PII (персональные данные), дисбаланс тематик, короткие или неполные диалоги, повторяющиеся паттерны.

2. Парсинг логов: извлечение сообщений и ролей

  • Определяем источники: JSON-логи, CSV, текстовые файлы, базы данных CRM.
  • Извлекаем каждое сообщение: временная метка, идентификатор оператора/клиента, текст.
  • Размечаем роли: оператор → assistant, клиент → user. Если лог содержит системные сообщения (например, «бота подключили»), их либо удаляем, либо помечаем как system.
  • Группируем в диалоги: по session_id или временному окну (обычно до 30 мин бездействия — новый диалог).
import pandas as pd

def parse_logs(filepath):
    df = pd.read_json(filepath)
    df['role'] = df['sender'].apply(lambda x: 'user' if x == 'client' else 'assistant')
    dialogs = df.groupby('session_id').apply(lambda g: g[['role', 'message']].to_dict('records'))
    return dialogs

Термин session_id — уникальный идентификатор сессии диалога, по которому можно собрать все сообщения одного разговора.

3. Очистка данных: удаление PII, шума и стандартизация

  • Удаление PII: имена, номера телефонов, email, адреса. Используем регулярные выражения или библиотеки типа presidio‑analyzer.
  • Удаление шума: лишние пробелы, HTML-теги, нечитаемые символы, повторяющиеся фразы («Алло?», «Вы меня слышите?»).
  • Стандартизация: приведение к нижнему регистру (кроме имён собственных), исправление распространённых опечаток (словарь замен).
  • Маскирование: заменить конкретные имена на [NAME], номера на [PHONE] — сохраняет структуру, не нарушая приватность.

Пример маскирования:

import re

def mask_pii(text):
    text = re.sub(r'\b\d{10,15}\b', '[PHONE]', text)  # номера телефонов
    text = re.sub(r'\b[A-Za-z0-[[9. Как вы обновляете документы в существующей RAG-системе|9]]._%+-]+@[A-Za-z0-[[9. Как вы обновляете документы в существующей RAG-системе|9]].-]+\.[A-Z|a-z]{[[2 Как вы решаете проблему lost in the middle при работе с длинными контекстами|2]],}\b', '[EMAIL]', text)
    return text

4. Форматирование в инструкционные пары

Для fine-tuning необходимо представить диалог как последовательность сообщений с явными ролями. Популярные форматы:

ФорматПример
ChatML`<
Alpaca{"instruction": "Ответь как оператор поддержки", "input": "Текст клиента", "output": "Текст оператора"}

Выбор зависит от модели: Llama fine-tuning чаще использует ChatML, для меньших моделей (GPT‑2) — Alpaca.

Каждый диалог может быть разбит на несколько пар user → assistant (каждая реплика оператора — отдельный пример). Важно сохранять контекст предыдущих реплик (включать в input всю историю до текущего ответа).

5. Фильтрация и балансировка датасета

  • Удаление коротких диалогов: диалоги, где суммарная длина сообщений < 10 слов или где ответ оператора < 3 слов — они не несут обучающей информации.
  • Балансировка по длине: слишком длинные диалоги (более 2048 токенов) обрезать или разбивать на части.
  • Балансировка по тематикам: если 80% диалогов — возврат товара, а 20% — консультация, модель будет плохо отвечать на редкие запросы. Используем стратифицированную выборку или оверсэмплинг меньших классов.
  • Удаление дубликатов: одинаковые запросы клиента могут свидетельствовать о ботах или копипастах.

6. Аугментация данных для разнообразия

Даже если у вас много диалогов, они могут быть однотипны. Аугментация помогает модели лучше обобщать:

  • Перефразирование через LLM: для каждого запроса клиента сгенерировать 1–3 варианта перефразирования (с помощью GPT‑4 или другой сильной модели), сохраняя ту же реплику оператора.
  • Обратный перевод: перевести клиентский запрос на другой язык и обратно (напр., русский → английский → русский) — порождает лексические вариации.
  • Синонимическая замена: замена ключевых слов (например, «проблема» → «неполадка») с помощью WordNet или BERT‑маскировки.

Важно аугментация не должна менять intent (намерение) клиента, иначе модель выучит неверные паттерны.

7. Разметка качества и автоматическая оценка

Не все диалоги одинаково полезны. Часть может содержать грубые ошибки оператора или неразрешённые проблемы. Применяем LLM-as-a-judge:

  • Отправляем диалог мощной LLM с промптом: «Оцени качество ответа оператора от 1 до 5 по шкале: полезность, полнота, корректность».
  • Отбрасываем диалоги с оценкой < 3.
  • Можно дополнительно использовать метрики BLEU, ROUGE, BERTScore для сравнения ответов оператора с эталоном (если он есть), но в сырых диалогах эталона нет — только субъективная оценка.

Пример промпта для LLM-as-a-judge:

Система: Ты — эксперт по качеству поддержки. Оцени ответ оператора по шкале 1-5.

Пользователь: {запрос клиента}
Ассистент: {ответ оператора}

Оценка (только число):

8. Токенизация и подготовка к fine-tuning

После очистки данных необходимо преобразовать текст в токены:

  • Выбираем токенизатор, соответствующий базовой модели (например, bert‑base‑uncased или tokenizer из transformers для Llama).
  • Обрезаем по max_length (обычно 512–2048 токенов).
  • Применяем padding до фиксированной длины внутри батча.
  • Для instruction‑tuning важно, чтобы токены ответа (assistant) были замаскированы при подсчёте loss — используется маска attention_mask и labels (set labels = -100 для токенов промпта).
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("mistralai/Mistral-7B-v0.1")

def tokenize_pair(user_input, assistant_output):
    # Формат ChatML
    prompt = f"<|im_start|>user\n{user_input}<|im_end|>\n<|im_start|>assistant\n"
    full_text = prompt + assistant_output + "<|im_end|>"
    tokenized = tokenizer(full_text, truncation=True, max_length=1024, padding="max_length")
    return tokenized

9. Проверка на утечки и смещения (bias)

  • Разделение train/val/test: по времени (более старые диалоги — train, новые — val/test) или по session_id, чтобы избежать пересечения контекста.
  • Стратификация: сохранять пропорции тематик в каждой выборке.
  • Проверка на утечки: убедиться, что один и тот же запрос не попал одновременно в train и test (например, через хэширование фраз).
  • Детекция смещений (bias): если модель будет учиться на диалогах, где операторы всегда женского пола, это может привести к гендерным стереотипам. Анализировать распределение атрибутов.

Пет-проект для закрепления

Задача Разработать пайплайн подготовки датасета из 10 000 неструктурированных чат-логов (файлы JSON) для fine-tuning небольшой модели (например, TinyLlama-1.1B).

Инструменты Python, pandas, transformers, presidio-analyzer, OpenAI API (для аугментации и оценки), Hugging Face Datasets.

Шаги:

  1. Загрузить 10 000 JSON-логов (можно синтезировать с помощью GPT).
  2. Распарсить в диалоги, назначить роли.
  3. Очистить от PII (регулярки + presidio).
  4. Отфильтровать короткие (< 5 реплик) и дублирующиеся диалоги.
  5. Преобразовать в формат ChatML.
  6. Сбалансировать по темам (определить темы через TF-IDF + KMeans).
  7. Аугментировать 20% запросов через перефразирование (GPT-4o-mini).
  8. Оценить качество ответов (LLM-as-a-judge), отбросить нижний квартиль.
  9. Токенизировать и сохранить в формате Hugging Face Dataset.
  10. Обучить TinyLlama на полученном датасете, сравнить с baseline (исходная модель) по метрикам ROUGE и BERTScore.

Ожидаемый результат Работающий пайплайн, который из сырых логов создаёт готовый датасет для fine-tuning; обученная модель, дающая осмысленные ответы в стиле поддержки.

Связь с другими вопросами

ВопросТема
31Какие методы fine-tuning существуют (full, LoRA, QLoRA)?
33Как выбрать базовую модель под задачу fine-tuning?
34Как оценить качество fine-tuned модели?
35Data augmentation для fine-tuning: стратегии и риски.
40Обработка персональных данных (PII) в обучающих датасетах.
45Форматы instruction tuning (ChatML, Alpaca, ShareGPT) – сравнение.

Навигация