Очистка данных для fine-tuning LLM: пайплайн и метрики

Пайплайн очистки данных для fine-tuning LLM

Направления AI-разработки

Часто задаваемые вопросы

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1440
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1301
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    997
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1264
  • image_logo-advance_0.webp
    Разработка логотипа компании B2B Advance
    712
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    1002

Пайплайн очистки данных для fine-tuning LLM

Представьте: вы собрали 100 000 примеров для дообучения LLaMA 3, но модель выдаёт бессвязные ответы и галлюцинирует на каждом третьем запросе. Причина — грязные данные: 40% дубликатов, 15% содержат персональные данные, ещё 10% — токсичный контент. Без качественной очистки fine-tuning не даст нужного результата.

Мы разработали пайплайн, который за 10–14 дней превращает сырой датасет в чистый, готовый к обучению. MinHash LSH для дедупликации работает в 10 раз быстрее попарного сравнения при поиске near-duplicates на датасетах из 50 000 примеров. А фильтрация токсичности через Detoxify снижает вероятность нежелательных ответов модели на 25% по сравнению с простым регулярным выражением.

Почему стандартная очистка не подходит для LLM?

Тексты для fine-tuning содержат специфические артефакты: HTML-теги (если собирали с веба), Unicode-вариации, мета-комментарии моделей вроде «As an AI language model...». Простое удаление знаков препинания не решает проблему. Нужна многослойная фильтрация с учётом контекста. Например, PII-детекция требует не только регекспов, но и NER-модели (spaCy), чтобы найти «Джон Доу, ул. Ленина» — это не менее важно, чем номера карт. Перед запуском пайплайна рекомендуется ознакомиться с best practices из документации Hugging Face Datasets.

Как мы строим пайплайн очистки

Пайплайн состоит из последовательных этапов, каждый из которых проверяет и трансформирует пример. Критично не переусердствовать: чрезмерная чистка снижает разнообразие данных.

import re import unicodedata from dataclasses import dataclass @dataclass class CleaningResult: original: str cleaned: str removed: bool removal_reason: str = None class TextCleaner: def clean(self, text: str) -> CleaningResult: cleaned = text # 1. Нормализация Unicode cleaned = unicodedata.normalize('NFKC', cleaned) # 2. Удаление HTML/XML тегов cleaned = re.sub(r'<[^>]+>', ' ', cleaned) # 3. Очистка URL (опционально — заменяем на placeholder) cleaned = re.sub( r'https?://[^\s]+', '[URL]', cleaned ) # 4. Нормализация пробелов cleaned = re.sub(r'\s+', ' ', cleaned).strip() # 5. Удаление повторяющихся символов (ааааааа → а) cleaned = re.sub(r'(.)\1{4,}', r'\1\1', cleaned) # Проверка на минимальную длину if len(cleaned.split()) < 3: return CleaningResult(text, cleaned, True, "too_short") return CleaningResult(text, cleaned, False) class DataFilter: def __init__(self): # Токсичность (можно использовать detoxify или fasttext) from detoxify import Detoxify self.toxicity_model = Detoxify('multilingual') def is_toxic(self, text: str, threshold: float = 0.7) -> bool: result = self.toxicity_model.predict(text) return result['toxicity'] > threshold def has_pii(self, text: str) -> bool: """Простая эвристика для PII детекции""" patterns = [ r'\b\d{3}-\d{2}-\d{4}\b', # SSN r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b', # Email r'\b(?:\+7|8)?[\s-]?\(?\d{3}\)?[\s-]?\d{3}[\s-]?\d{2}[\s-]?\d{2}\b', # RU phone r'\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b', # Credit card ] for pattern in patterns: if re.search(pattern, text): return True return False 

Пошаговая настройка пайплайна

  1. Определите пороги фильтрации. Для токсичности используйте threshold 0.7 — это даёт баланс между удалением плохого контента и сохранением полезного. Для дублей установите схожесть 0.8.
  2. Выберите алгоритм дедупликации. Для точных дублей — exact matching, для near-duplicates — MinHash LSH. SimHash подходит для потоковой обработки, но даёт больше ложных срабатываний.
  3. Запустите тестовый прогон на 1000 примерах. Проверьте метрики: количество удалённых, тип-токен-рацио, остаточную токсичность. Если всё в норме — запускайте полный датасет.

Очистка output полей

Ответы моделей-помощников часто содержат нежелательные вступления: «Certainly! Here is my response». Алгоритм определяет эти шаблоны и обрезает их, оставляя только полезный контент.

class OutputCleaner: def clean_output(self, output: str, task_type: str) -> tuple[str, bool]: cleaned = output.strip() # Удаление нежелательных фраз модели unwanted_starts = [ "As an AI language model", "As a helpful assistant", "I don't have access to real-time", "I cannot browse the internet", "Certainly! Here", "Of course! I'd be happy to", ] for phrase in unwanted_starts: if cleaned.lower().startswith(phrase.lower()): # Удаляем вступительную фразу cleaned = cleaned[len(phrase):].lstrip('.,! ') # Проверка: output не должен содержать meta-комментарии meta_indicators = [ "Note: This is a fictional", "[This response was", "Disclaimer:", ] for indicator in meta_indicators: if indicator in cleaned: idx = cleaned.find(indicator) cleaned = cleaned[:idx].strip() # Минимальная длина if len(cleaned.split()) < 5: return cleaned, True # Пометить для удаления return cleaned, False 

Детекция дублей разных уровней

Для точных дублей используем хеширование, для near-duplicates — MinHash LSH. Порог схожести 0.8 отсекает почти идентичные примеры, но сохраняет вариативность.

from datasketch import MinHash, MinHashLSH def find_near_duplicates(texts: list[str], threshold: float = 0.8) -> list[tuple]: """MinHash LSH для эффективного поиска near-duplicates O(n log n)""" lsh = MinHashLSH(threshold=threshold, num_perm=128) minhashes = {} for i, text in enumerate(texts): m = MinHash(num_perm=128) for word in text.lower().split(): m.update(word.encode('utf8')) lsh.insert(f"doc_{i}", m) minhashes[f"doc_{i}"] = m duplicates = [] for i, text in enumerate(texts): key = f"doc_{i}" result = lsh.query(minhashes[key]) result.remove(key) if result: duplicates.append((i, [int(r.split('_')[1]) for r in result])) return duplicates 

Сравнение методов дедупликации

Метод Скорость Точность Применение
Exact matching O(n) 100% Точные дубли
MinHash LSH O(n log n) ~95% Near-duplicates
SimHash O(n) ~90% Быстрая оценка

Статистика после очистки

После пайплайна обязательно проверяем метрики:

Метрика Норма Зачем
Удалено примеров 15–30% Контроль агрессивности очистки
Тип токенов >5 млн Достаточно для fine-tuning
Type-token ratio >0.5 Достаточное разнообразие
Покрытие задач >90% Все нужные сценарии
Токсичность <1% Безопасность модели

Типичный результат: из 50 000 сырых примеров после очистки остаётся 35 000–42 000 высококачественных. Снижение объёма на 15–30% — норма, и итоговое качество модели от этого только улучшается. По сравнению с грубой очисткой (только регекспы), точность fine-tuning возрастает на 15–20%. Частая проблема — несбалансированность классов: если в датасете 90% примеров с позитивным тоном, модель не научится отвечать на негативные запросы. Мы применяем стратифицированную выборку и аугментацию редких классов. Также важно удалять стоп-слова, характерные для LLM: 'As a language model', 'I cannot', 'I think'. Это снижает шум на 5–10%.

Что входит в работу

Мы подготавливаем полный пайплайн очистки под ваш датасет:

  • Анализ сырых данных (распределение длин, язык, токсичность)
  • Настройка фильтров под вашу задачу (RAG, генерация, классификация)
  • Дедупликация и удаление PII
  • Нормализация и токенизация
  • Отчёт с метриками и визуализациями
  • Документация пайплайна и конфигурация
  • Обучение вашей команды

Сроки — от 10 до 14 рабочих дней в зависимости от объёма. Свяжитесь с нами для оценки вашего проекта — мы гарантируем конфиденциальность и качество результата. Наш опыт: более 5 лет в NLP, более 20 проектов по fine-tuning моделей разного размера. Получите консультацию по очистке датасета — мы подготовим индивидуальный пайплайн.