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