Как вы обнаруживаете contamination (пересечение synthetic данных с тестовыми)?

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

Contamination (загрязнение данных) — это ситуация, когда синтетические данные, использованные для обучения или дообучения модели, частично или полностью совпадают с тестовыми данными. Это приводит к завышенным метрикам и нечестной оценке реального качества модели. Обнаружить contamination можно тремя основными методами: n-gram overlap (проверка совпадения последовательностей токенов), embedding similarity (косинусная близость векторных представлений) и perplexity anomaly (аномально низкая перплексия модели на тестовом примере). Комбинирование этих подходов даёт надёжную оценку.


1. Термин: Data contamination (загрязнение данных)

Data contamination — это нежелательное пересечение между обучающей и тестовой выборками. В контексте synthetic данных (данных, сгенерированных LLM или другими алгоритмами) contamination возникает, когда генератор случайно воспроизводит фрагменты тестового набора, либо когда синтетические данные создаются на основе тестовых (например, через промпты, содержащие тестовые примеры).

Почему это критично для RAG и RAG|Agentic RAG:

  • В RAG-системах contamination может скрыть проблемы retrieval: если тестовый запрос уже встречался в обучении, модель может «помнить» ответ, а не извлекать его из документов.
  • В Agentic RAG агенты могут генерировать синтетические данные в процессе работы (например, для few-shot обучения), и если эти данные пересекаются с тестовыми, оценка агента становится некорректной.

2. Почему contamination особенно актуальна для synthetic данных?

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

СценарийОписание
Прямое копированиеГенератор выдаёт текст, совпадающий с тестовым примером (например, вопрос-ответ из бенчмарка).
ПарафразированиеСинтетический пример перефразирует тестовый, сохраняя смысл и ключевые n-граммы.
Тематическое совпадениеТестовый и синтетический примеры описывают одну и ту же редкую сущность или факт, что может быть ошибочно принято за contamination.

3. Метод 1: N-gram overlap

N-gram — это непрерывная последовательность из n токенов (слов или символов). N-gram overlap — доля n-грамм, общих для двух текстов.

Алгоритм

  1. Разбить каждый пример (train и test) на n-граммы (обычно n=5 для слов, n=13 для символов).
  2. Для каждого тестового примера вычислить максимальный overlap с любым обучающим примером.
  3. Если overlap > порога (например, 50% для 5-грамм), пример считается подозрительным.

Пример кода на Python

from collections import Counter

def ngram_overlap(text1, text2, n=5):
    tokens1 = text1.split()
    tokens2 = text2.split()
    if len(tokens1) < n or len(tokens2) < n:
        return 0.0
    ngrams1 = set(zip(*[tokens1[i:] for i in range(n)]))
    ngrams2 = set(zip(*[tokens2[i:] for i in range(n)]))
    if not ngrams2:
        return 0.0
    intersection = ngrams1 & ngrams2
    return len(intersection) / len(ngrams2)

# Пример
train_text = "The cat sat on the mat"
test_text = "The cat sat on the mat and smiled"
print(ngram_overlap(train_text, test_text, n=3))  # ~0.75

Пороги и интерпретация

OverlapВердикт
> 80%Почти наверняка contamination (прямое копирование)
50–80%Высокая вероятность (возможно парафразирование)
20–50%Требуется дополнительная проверка
< 20%Скорее всего нет contamination

Ограничения не учитывает семантику — два текста могут быть разными по смыслу, но иметь много общих n-грамм (например, стоп-слова). Рекомендуется предварительно удалять стоп-слова или использовать skip-grams.


4. Метод 2: Embedding similarity

Embedding — это плотное векторное представление текста, сохраняющее семантическую близость. Embedding similarity (обычно косинусная близость) измеряет угол между векторами.

Алгоритм

  1. Получить эмбеддинги для всех обучающих и тестовых примеров (например, с помощью sentence-transformers).
  2. Для каждого тестового эмбеддинга найти максимальную косинусную близость с любым обучающим.
  3. Если близость > порога (например, 0.95), пример подозрителен.

Пример кода

from sentence_transformers import SentenceTransformer
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np

model = SentenceTransformer('all-MiniLM-L6-v2')
train_embs = model.encode(train_texts)
test_embs = model.encode(test_texts)

threshold = 0.95
contaminated = []
for i, test_emb in enumerate(test_embs):
    sims = cosine_similarity([test_emb], train_embs)[0]
    max_sim = np.max(sims)
    if max_sim > threshold:
        contaminated.append((i, max_sim))

Пороги

Косинусная близостьИнтерпретация
> 0.98Почти идентичный смысл (contamination)
0.95–0.98Очень похожий смысл (возможен парафраз)
0.90–0.95Семантически близко, но может быть случайным
< 0.90Скорее всего разные примеры

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


5. Метод 3: Perplexity anomaly

Perplexity (перплексия) — мера того, насколько модель «удивлена» текстом. Низкая перплексия означает, что текст хорошо предсказывается моделью. Если тестовый пример был в обучении, модель будет иметь аномально низкую перплексию на нём.

Алгоритм

  1. Вычислить перплексию для каждого тестового примера с помощью той же модели, которая использовалась для генерации синтетики (или целевой модели).
  2. Построить распределение перплексий по всем тестовым примерам.
  3. Примеры с перплексией ниже 5-го перцентиля (или ниже порога, определённого эмпирически) считаются подозрительными.

Пример кода (с использованием transformers):

from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
import numpy as np

model_name = "gpt2"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name)

def compute_perplexity(text):
    inputs = tokenizer(text, return_tensors="pt")
    with torch.no_grad():
        outputs = model(**inputs, labels=inputs["input_ids"])
        loss = outputs.loss
    return torch.exp(loss).item()

perplexities = [compute_perplexity(t) for t in test_texts]
threshold = np.percentile(perplexities, 5)
contaminated = [i for i, p in enumerate(perplexities) if p < threshold]

Интерпретация Если модель показывает на тестовом примере перплексию значительно ниже, чем на большинстве других тестовых примеров, это указывает на то, что пример был «заучен» во время обучения (возможно, через синтетические данные).

Ограничения метод требует доступа к самой модели (или её логам). Кроме того, низкая перплексия может быть вызвана простотой текста, а не contamination.


6. Комбинированный подход

Ни один метод не идеален. Рекомендуется комбинировать все три:

  1. Фильтр 1 (n-gram): отсеять примеры с overlap > 50%.
  2. Фильтр 2 (embedding): для оставшихся проверить косинусную близость > 0.95.
  3. Фильтр 3 (perplexity): для подозрительных по первым двум фильтрам проверить перплексию.

Взвешенная оценка можно присвоить каждому методу вес и вычислить общий score contamination. Например:

score = 0.4 * (ngram_overlap) + 0.4 * (embedding_sim) + 0.2 * (1 - normalized_perplexity)

Если score > порога (например, 0.7) — пример считается contaminated.


7. Инструменты и библиотеки

ИнструментНазначение
NLTK / spaCyТокенизация, выделение n-грамм
scikit-learnКосинусная близость, метрики
sentence-transformersПолучение эмбеддингов
transformersВычисление перплексии
datasets (Hugging Face)Загрузка и сравнение датасетов
contamination-detection (самописный модуль)Обёртка над методами

8. Практические рекомендации по предотвращению contamination

  • Разделение данных до генерации синтетики сначала фиксируйте тестовый набор, затем генерируйте синтетические данные на основе только обучающего.
  • Контроль промптов не включайте в промпт для генерации синтетики примеры из тестового набора.
  • Дедупликация после генерации синтетики прогоните её через описанные методы и удалите подозрительные примеры.
  • Использование hold-out выделите часть тестового набора как «чистый» для финальной оценки, а contamination проверяйте на другом подмножестве.

9. Ограничения методов

  • Ложные срабатывания редкие, но возможны, если два разных текста случайно имеют высокий n-gram overlap (например, оба содержат длинную цитату из общего источника).
  • Вычислительная сложность для больших датасетов попарное сравнение всех train-test может быть дорогим (O(N_train * N_test)). Решение: использовать индексы (FAISS) для embedding similarity.
  • Нечувствительность к перефразированию n-gram метод не ловит семантические парафразы, а embedding метод может не заметить точное совпадение, если модель эмбеддингов недостаточно точна.
  • Зависимость от модели perplexity anomaly требует той же модели, которая обучалась на синтетике; если модель меняется, метрика становится неприменимой.

10. Связь с Agentic RAG

В Agentic RAG агенты могут динамически генерировать синтетические данные (например, для few-shot примеров или для обучения reward model). Если эти данные пересекаются с тестовыми запросами, оценка агента становится невалидной. Особенно опасно, когда агент использует retrieval-augmented generation и contamination скрывает проблемы с поиском: модель может «вспомнить» ответ из обучения, а не извлечь его из документов. Поэтому перед развёртыванием агента необходимо провести полную проверку contamination на всех этапах пайплайна.


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

Задача Написать скрипт для обнаружения contamination в датасете синтетических вопросов, сгенерированных для RAG-системы.

Инструменты Python, nltk, sentence-transformers, transformers, scikit-learn, pandas.

Шаги:

  1. Загрузите тестовый набор вопросов (например, из SQuAD или собственного датасета).
  2. Сгенерируйте синтетические вопросы с помощью LLM (например, GPT-2) на основе обучающих документов.
  3. Реализуйте три функции:
    • ngram_overlap_score(train_texts, test_texts, n=5)
    • embedding_similarity_score(train_texts, test_texts, model_name='all-MiniLM-L6-v2')
    • perplexity_anomaly_score(test_texts, model_name='gpt2')
  4. Примените комбинированный порог (например, overlap > 0.5 ИЛИ similarity > 0.95 ИЛИ perplexity < 5-го перцентиля).
  5. Выведите список индексов подозрительных примеров и их оценки.

Ожидаемый результат Вы получите отчёт вида:

Contamination report:
- Пример 12: overlap=0.82, sim=0.97, perplexity=15.3 (ниже порога 20.1) -> CONTAMINATED
- Пример 45: overlap=0.12, sim=0.88, perplexity=45.2 -> OK
...
Всего contaminated: 23 из 1000 (2.3%)

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

ВопросТема
5Оценка качества retrieval в RAG
12Как генерировать синтетические данные для RAG
18Data leakage в LLM
45Fine-tuning на синтетических данных
102Метрики для оценки RAG-систем
688Как избежать переобучения при генерации синтетики

Навигация