Перейти к содержанию
Публикация AiManual

Дедипликация данных для LLM: как BigCode очищает 1.4 ТБ кода и почему это критично

Как BigCode удалил дубликаты из 1.4 ТБ кода за 4 часа с бюджетом $15/час: разбор MinHash + LSH, оптимальные параметры и влияние на HumanEval. Практические реком

Коротко

Что будет в материале

  1. 01

    Почему дубликаты в обучающих данных - проблема для LLM

  2. 02

    Near-deduplication: что это и как работает MinHash + LSH

  3. 03

    Кейс BigCode: дедупликация 1.4 ТБ кода за 4 часа

  4. 04

    Влияние дедупликации на качество код-моделей

Дубликаты в обучающем датасете снижают качество языковых моделей. Проект BigCode показал, как удалить near-duplicates из 1.4 ТБ исходного кода за 4 часа с бюджетом $15/час, используя MinHash и Locality Sensitive Hashing. Результат: агрессивная дедупликация улучшает downstream-метрики код-моделей даже на меньшем объёме данных. Порог похожести 0.7 и шинглы из 5 токенов дают оптимальный баланс между чистотой датасета и сохранением полезных вариаций кода.

В статье разберём механику MinHash + LSH, параметры дедупликации в BigCode, влияние на HumanEval и MBPP, а также ограничения подхода. Отдельно коснёмся защиты от утечки бенчмарков в тренировочные данные - этот шаг критичен для честной оценки модели.

Почему дубликаты в обучающих данных - проблема для LLM

Дубликаты в корпусе для предобучения создают три проблемы. Первая - переобучение: модель запоминает частые примеры вместо обобщения закономерностей. Вторая - смещение оценки: валидационные метрики завышаются, если тестовые сэмплы встречаются в обучающей выборке. Третья - неэффективный расход вычислительных ресурсов: GPU-часы тратятся на повторную обработку идентичных или почти идентичных последовательностей.

Для кода проблема стоит острее, чем для естественного языка. Репозитории на GitHub массово копируются: форки, шаблоны, библиотеки с открытым исходным кодом, автогенерированный код. Исследователи The Pile и GPT-3 фиксировали, что значительная доля обучающих данных содержит повторы. В корпусах кода доля дубликатов может достигать десятков процентов. Если не удалить их до обучения, модель будет генерировать код с ограниченным разнообразием и хуже справляться с незнакомыми задачами.

BigCode подошла к проблеме системно. Вместо точного сопоставления строк применили near-deduplication - поиск похожих, а не идентичных документов. Это позволило отсеять клоны с минимальными изменениями: переименованными переменными, изменёнными комментариями, сдвинутыми отступами.

Near-deduplication: что это и как работает MinHash + LSH

Near-deduplication решает задачу поиска документов с высокой степенью сходства в огромных коллекциях. Точное сравнение каждого документа с каждым требует O(n²) операций - непрактично для миллионов файлов. MinHash сжимает документ до компактной сигнатуры, а LSH группирует похожие сигнатуры для быстрого поиска кандидатов.

Шинглы и сигнатуры: представление кода для сравнения

Шинглы - это n-граммы, на которые разбивается документ. Для кода используют последовательности токенов, а не символов: токенизация учитывает синтаксис языка программирования. Размер шингла 5–8 токенов считается рабочим диапазоном. Слишком короткие шинглы дают много ложных совпадений, слишком длинные пропускают мелкие изменения.

MinHash преобразует множество шинглов документа в сигнатуру фиксированной длины. Алгоритм применяет несколько хеш-функций к каждому шинглу и для каждой функции сохраняет минимальное значение хеша. Если два документа похожи, их сигнатуры совпадают с вероятностью, равной коэффициенту Жаккара исходных множеств шинглов. Пример для Python:

from datasketch import MinHash

def tokenize_code(code: str, shingle_size: int = 5):
    tokens = code.split()
    return [tuple(tokens[i:i+shingle_size]) for i in range(len(tokens) - shingle_size + 1)]

m = MinHash(num_perm=256)
for shingle in tokenize_code(code_snippet):
    m.update(" ".join(shingle).encode("utf-8"))

signature = m.digest()  # 256-мерный вектор

Число хеш-функций (num_perm) определяет точность оценки сходства. BigCode использовала 256 перестановок - это даёт стандартную ошибку около 3% при оценке коэффициента Жаккара.

Locality Sensitive Hashing: быстрый поиск похожих

LSH ускоряет поиск похожих сигнатур. Сигнатура разбивается на полосы (bands), каждая полоса хешируется. Документы с одинаковым хешем хотя бы одной полосы попадают в одну корзину и становятся кандидатами на сравнение. Число полос управляет порогом срабатывания: больше полос - выше чувствительность, больше ложных кандидатов; меньше полос - ниже чувствительность, выше шанс пропустить похожие пары.

Связь между параметрами описывается формулой: вероятность того, что два документа с сходством s попадут в одну корзину, равна 1 − (1 − s^r)^b, где r - число строк в полосе, b - число полос. Для BigCode параметры подбирались так, чтобы порог сходства около 0.7 давал высокую вероятность обнаружения пары.

Кейс BigCode: дедупликация 1.4 ТБ кода за 4 часа

BigCode - открытый проект по созданию код-моделей, включая StarCoder. Исходный датасет: 1.4 ТБ кода, собранного с GitHub. Цель - удалить near-duplicates до обучения, чтобы модель не тратила ёмкость на запоминание клонов.

Процесс построен на библиотеке Datasketch, реализующей MinHash LSH. Параметры: шинглы из 5 токенов, 256 хеш-функций, порог сходства 0.7. Вычисления запускались на spot instances с бюджетом $15/час. Полный проход занял 4 часа. Итоговая стоимость - около $60, что на порядки ниже затрат на обучение модели.

Эффективность достигнута за счёт распределённой обработки: сигнатуры вычислялись параллельно на множестве воркеров, LSH-индексация выполнялась инкрементально. Такой подход воспроизводим для команд, которые готовят собственные датасеты кода.

Параметры дедупликации: поиск баланса

Порог похожести - главный trade-off. Слишком агрессивная дедупликация (порог 0.5) удаляет полезные вариации: разные реализации одной задачи, альтернативные стили кодирования, адаптации под специфичные окружения. Слишком мягкая (порог 0.9) оставляет клоны, которые различаются лишь незначительными правками.

Эксперименты BigCode с порогами 0.5, 0.7 и 0.9 показали: порог 0.7 даёт лучший баланс. При нём удаляется значительная доля дубликатов, а downstream-метрики на HumanEval и MBPP улучшаются. Порог 0.5 приводил к потере разнообразия и снижению качества на редких языках программирования. Порог 0.9 оставлял слишком много повторов, и модель переобучалась на частых паттернах.

Влияние дедупликации на качество код-моделей

Прямое сравнение моделей, обученных на полном и дедуплицированном датасете, подтверждает пользу очистки. Модель на меньшем, но чистом корпусе показывает лучшие результаты на downstream-задачах. На HumanEval и MBPP прирост составил несколько процентных пунктов по pass@1.

Причина - устранение переобучения. Когда модель видит один и тот же фрагмент кода десятки раз, она запоминает его вместо того, чтобы выучивать общие правила синтаксиса и семантики. После дедупликации каждый пример вносит новый сигнал, и градиенты эффективнее обновляют веса. Меньший датасет с высоким разнообразием даёт лучшее обобщение, чем большой корпус с повторами.

Для разработчиков, которые выбирают между объёмом данных и их чистотой, вывод однозначен: дедупликация окупается. Подробнее о том, как BigCode использовала очищенный датасет для обучения StarCoder, читайте в разборе архитектуры StarCoder.

Ограничения MinHash: почему нужна дополнительная фильтрация

MinHash удаляет синтаксические дубликаты, но не решает всех проблем качества данных. Остаются две задачи: фильтрация некачественного кода и защита от утечки бенчмарков.

Некачественный код - это автогенерированные файлы, обфусцированные фрагменты, код без лицензии, слишком короткие или бессмысленные файлы. BigCode применяла дополнительные эвристики: удаление файлов без лицензии, отсев по минимальной длине, исключение бинарных файлов и конфигурационных артефактов. Эти шаги снижают шум в обучающей выборке.

Защита от утечки бенчмарков

Утечка бенчмарков - ситуация, когда задачи из тестового набора попадают в обучающие данные. Модель запоминает ответы, и метрики на HumanEval или MBPP перестают отражать реальную способность к обобщению. BigCode проверяла датасет на наличие точных совпадений с задачами бенчмарков и удаляла похожие фрагменты.

Методы защиты: поиск точных совпадений строк, сравнение сигнатур MinHash с сигнатурами задач бенчмарка, ручная проверка подозрительных кластеров. Такой подход обязателен для любого проекта, который публикует метрики на публичных бенчмарках. Без него оценка модели будет завышенной и вводящей в заблуждение.

Проблема утечки шире, чем кажется. Evaluation awareness - способность модели распознавать тестовые сценарии - завышает метрики даже без прямого копирования задач. Подробнее об этом механизме в статье об evaluation awareness.

Практические рекомендации по дедупликации для ваших проектов

Пять шагов для внедрения near-deduplication в собственный пайплайн подготовки данных.

  1. Оцените масштаб дубликатов. Запустите MinHash на репрезентативной выборке и постройте распределение попарных сходств. Если медианное сходство выше 0.3, дедупликация даст заметный эффект.
  2. Выберите параметры. Шинглы из 5–8 токенов, 256 хеш-функций, порог 0.7 - проверенная отправная точка для кода. Для естественного языка размер шингла можно увеличить до 8–10.
  3. Используйте готовые библиотеки. Datasketch для Python реализует MinHash LSH из коробки. SimHash подходит для быстрой оценки сходства больших документов.
  4. Проведите дополнительную фильтрацию. Удалите файлы без лицензии, слишком короткие, автогенерированные. Проверьте кодировки и бинарные артефакты.
  5. Проверьте утечку бенчмарков. Сравните сигнатуры обучающих документов с сигнатурами задач из HumanEval, MBPP и других используемых метрик. Удалите совпадения.

Минимальный рабочий пример на Python:

from datasketch import MinHash, MinHashLSH

# Параметры
SHINGLE_SIZE = 5
NUM_PERM = 256
THRESHOLD = 0.7

lsh = MinHashLSH(threshold=THRESHOLD, num_perm=NUM_PERM)

def get_minhash(code: str):
    tokens = code.split()
    shingles = [" ".join(tokens[i:i+SHINGLE_SIZE]) for i in range(len(tokens) - SHINGLE_SIZE + 1)]
    m = MinHash(num_perm=NUM_PERM)
    for s in shingles:
        m.update(s.encode("utf-8"))
    return m

# Индексация документов
for doc_id, code in enumerate(code_files):
    lsh.insert(doc_id, get_minhash(code))

# Поиск дубликатов для нового документа
query_minhash = get_minhash(new_code)
result = lsh.query(query_minhash)
print(f"Похожие документы: {result}")

Для больших датасетов запускайте вычисления распределённо. Spot instances с почасовой оплатой снижают стоимость на порядок по сравнению с on-demand. Опыт BigCode показывает: 1.4 ТБ обрабатываются за 4 часа при бюджете $15/час.

Если вы работаете с открытыми датасетами кода, обратите внимание на The Stack v3 от Hugging Face - крупнейший открытый корпус исходного кода объёмом 114 ТБ. Подробнее в обзоре The Stack v3.

Дедупликация - не разовая акция, а этап пайплайна. При обновлении датасета повторяйте процедуру, отслеживайте распределение сходств и корректируйте порог под специфику данных. Чистый корпус окупается качеством модели и достоверностью метрик.

Подписаться на канал