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

Проектируем и внедряем системы искусственного интеллекта: от прототипа до production-ready решения. Наша команда объединяет экспертизу в машинном обучении, дата-инжиниринге и MLOps, чтобы AI работал не в лаборатории, а в реальном бизнесе.
Показано 1 из 1Все 1564 услуг
Очистка данных для fine-tuning LLM: пайплайн и метрики
Средний
~3-5 дней
Часто задаваемые вопросы

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

Этапы разработки AI-решения

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

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

Пайплайн очистки данных для 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 моделей разного размера. Получите консультацию по очистке датасета — мы подготовим индивидуальный пайплайн.

Data Engineering для ML: пайплайны, разметка и качество данных

«У нас много данных» — фраза, которая на деле часто означает «у нас много сырых логов в S3, которые никто не трогал два года». Перед тем как обучить модель, нужно понять, что вообще есть: какова структура, есть ли дубли, как часто меняется схема, насколько репрезентативна выборка.

Data Engineering для ML — не просто ETL. Это построение воспроизводимой инфраструктуры данных, которая делает обучение моделей надёжным, а переобучение — предсказуемым. По опыту нашей команды (8 лет в дата-инжиниринге, более 30 проектов в ML) каждая вторая проблема в продакшене связана не с архитектурой модели, а с качеством данных.

ETЛ-пайплайны для ML: чем отличаются от BI

ETL для аналитики и ETL для ML — разные задачи. В аналитике важна агрегация, в ML — индивидуальные записи с историей. В аналитике train/val/test split не нужен, в ML — критичен. В аналитике skew данных мешает интерпретации, в ML — напрямую влияет на качество модели.

Инструменты. Apache Spark (Wikipedia) для больших объёмов (10GB+): PySpark с DataFrames, оптимизации через partitioning и caching. dbt для трансформаций поверх DWH (Snowflake, BigQuery, Redshift) — декларативно, версионируется, тестируется. Pandas + Polars для объёмов до нескольких GB — Polars в 5-10x быстрее Pandas на типичных трансформациях.

Temporal splits. Для ML важно, что split по времени, а не случайный. Если данные временные (транзакции, события пользователей), случайный split даёт data leakage: модель видит «будущие» данные при обучении. Правило: train на периоде T1-T2, validation на T2-T3 (с gap для предотвращения leakage), test на T3-T4. Неправильный split может стоить 10–15% качества модели на валидации.

Инкрементальные пайплайны. Модель переобучается еженедельно на новых данных. Нужен пайплайн, который инкрементально добавляет новые записи к обучающей выборке, не перегружая всё с нуля. Delta Lake или Apache Iceberg — форматы с ACID-транзакциями, Change Data Capture, time travel.

Как избежать training-serving skew с помощью Feature Store

Feature Store решает проблему рассинхронизации между обучением и инференсом. Самая коварная ошибка в ML-инфраструктуре — training-serving skew: признак считается по-разному в обучении и в продакшене. Модель учится на «правильных» данных, а инференс получает другие.

Feast (open source) — офлайн store на Parquet/Delta в S3 для обучения, онлайн store на Redis для low-latency инференса (<10ms). Feature definitions как Python-код:

from feast import FeatureView, Field
from feast.types import Float32, Int64

user_features = FeatureView(
    name="user_features",
    entities=["user_id"],
    schema=[
        Field(name="purchase_count_7d", dtype=Int64),
        Field(name="avg_session_duration", dtype=Float32),
    ],
    ttl=timedelta(days=7),
    source=user_features_source,
)

Один definition, используется везде. Нет расхождений.

Потоковые признаки. Когда признак должен обновляться в реальном времени (количество транзакций за последние 10 минут), нужна потоковая обработка. Apache Kafka + Apache Flink или Kafka Streams для вычисления признаков в реальном времени → запись в онлайн store. Сложнее, дороже, нужно только когда staleness признаков критична для качества.

Разметка данных: как не потратить бюджет впустую

Разметка — самая трудоёмкая и недооцениваемая часть ML-проекта. Плохо размеченные данные не исправит никакая архитектура.

Label Studio — open source, поддерживает разметку изображений (bounding box, polygon, segmentation), текста (NER, классификация), аудио, видео. Поднимается за 10 минут через Docker. Для небольших команд — первый выбор.

Оценка качества разметки. Inter-annotator agreement — насколько согласны разметчики между собой. Cohen's Kappa > 0.8 — хорошо, 0.6-0.8 — приемлемо, < 0.6 — задача неоднозначна или инструкция плохая. Пересечение разметок (10-20% примеров размечают два независимых аннотатора) — обязательная практика.

Active learning. Не размечать случайные примеры, а выбирать те, на которых модель наиболее неуверена (low confidence, high uncertainty). Позволяет добиться того же качества при 50-70% объёма разметки. Modals, Prodigy, Label Studio поддерживают active learning workflows. На одном из проектов для NLP мы сократили бюджет на разметку в 2,5 раза за счёт active learning.

Синтетические данные. Когда реальных данных мало или получить их дорого. Для CV: рендеринг в Blender/Unity с реалистичными текстурами (domain randomization). Для NLP: parafrase через LLM, backtranslation. Риск: модель обучается на distribution синтетических данных, а не реальных — нужна осторожность и проверка на реальном holdout.

Качество данных: валидация и мониторинг

Great Expectations — de facto стандарт для data validation в ML-пайплайнах. Expectations — это декларативные утверждения о данных: «колонка age содержит значения от 0 до 120», «колонка user_id не содержит null», «распределение amount не отклоняется более чем на 20% от baseline». Запускается в пайплайне, при провале — блокирует прохождение.

Pandera — Pythonic alternative для pandas/polars DataFrames. Schema-based validation с type hints:

import pandera as pa

schema = pa.DataFrameSchema({
    "user_id": pa.Column(int, nullable=False),
    "score": pa.Column(float, pa.Check.between(0, 1)),
    "label": pa.Column(str, pa.Check.isin(["positive", "negative", "neutral"])),
})

Data freshness. Модель ожидает данные за последние N дней. ETL упал, данные не обновились — модель использует устаревшие признаки. Мониторинг свежести данных: timestamp последней записи в каждой таблице, алерт при задержке > порога.

Дедупликация. Дубликаты в обучающей выборке завышают метрики (одни и те же примеры в train и val) и искажают веса модели. MinHash LSH для приближённой дедупликации больших датасетов. Для точной — хэш по нормализованному контенту.

Инструменты валидации: сравнение

Инструмент Область применения Когда выбирать
Great Expectations Универсальная, таблицы, пайплайны Большие команды, много метаданных
Pandera pandas/polars DataFrames Python-centric проекты, type hints
Deequ Apache Spark, большие данные Если пайплайн уже на Spark

Хранилища и форматы

Формат Лучше для Особенности
Parquet Батчевое обучение, аналитика Columnar, эффективное сжатие
Delta Lake Инкрементальные апдейты, ACID Time travel, schema evolution
Apache Iceberg Enterprise, multi-engine Лучший catalog, hidden partitioning
HDF5 Числовые массивы (CV датасеты) Иерархическая структура
TFDS / datasets Стандартизованные ML датасеты Hugging Face datasets — удобен для NLP

Для большинства ML-проектов на старте: Parquet в S3 + DVC для версионирования. Delta Lake или Iceberg — когда появляется потребность в инкрементальных обновлениях или time travel.

Что входит в проект по дата-инжинирингу для ML

Мы предоставляем полный цикл:

  • Аудит существующих данных и пайплайнов (1 неделя).
  • Проектирование архитектуры: выбор инструментов, форматов, способов разметки.
  • Реализация ETL/ELT пайплайна с валидацией и мониторингом.
  • Документация кода и процессов (model card, data card).
  • Обучение вашей команды работе с пайплайном.
  • SLA на сопровождение и поддержку.

Как мы строим пайплайн: пошагово

  1. Аудит существующих данных. Профилирование: ydata-profiling (бывший pandas-profiling) генерирует HTML-репорт со статистиками, дистрибуциями, корреляциями, missing values за минуты.
  2. Проектирование пайплайна. Определяем источники данных, частоту обновления, требования к latency признаков, объёмы.
  3. Реализация и тестирование. Unit-тесты на трансформации, integration-тесты на пайплайн, data validation через Great Expectations.
  4. Деплой и мониторинг. Алерты на freshness, quality checks, аномалии в объёмах данных.

Почему стоит доверить это нам

Мы занимаемся дата-инжинирингом и ML с 2016 года. За это время реализовали более 40 проектов — от построения пайплайнов для NLP-моделей до разметки датасетов для компьютерного зрения. Гарантируем воспроизводимость пайплайнов и полную прозрачность процессов. В каждом проекте используем инструменты с открытым исходным кодом, чтобы вы не были привязаны к вендору.

Свяжитесь с нами для бесплатного аудита ваших данных — оценим текущий пайплайн и предложим roadmap. Закажите построение ML-пайплайна под ключ.