Как вы fine-tune reranker (cross-encoder) для RAG? Как генерировать hard negatives?
Краткий тезис
Тонкая настройка (fine-tune) cross-encoder (также известного как reranker) для RAG-пайплайна — это процесс дообучения модели, которая принимает на вход пару (query, document) и выдаёт скалярную оценку релевантности. Ключевая сложность — генерация качественных hard negatives (сложных отрицательных примеров), которые модель должна научиться отличать от релевантных. Обычно hard negatives добываются с помощью ретривера (например, BM25 или dense retriever), при этом ошибки первого этапа (false positives) становятся ценными обучающими примерами. Функция потерь — pairwise ranking loss или listwise/cross-entropy.
-----|----------| | Вход | Пара (question, passage) — до 512 токенов | | Выход | Оценка релевантности (0..1) или логит-бинарный | | Параметры | Обычно 50–300M, но могут быть до 1B+ | | Скорость | ~1–10 ms на пару, но линейно от числа документов | | Использование в RAG | После ретривера (top-k, обычно 50–200) ранжирует, оставляя top-n (1–10) |
При fine-tuning мы обновляем веса как трансформера, так и головы, используя размеченные пары (query, doc, label). Однако обычные положительные и случайные отрицательные примеры не дают достаточной трудности — поэтому нужны hard negatives.
2. Hard negatives: документы, релевантные, но не топ-1 от BM25
Hard negatives — это документы, которые:
- не являются релевантными для запроса (по мнению человека или золотого стандарта);
- но при этом имеют высокое сходство с запросом (по лексическим или семантическим признакам);
- часто попадают в топ ретривера, обманывая наивные модели.
Типичный способ генерации:
- Взять обучающий пример (query, positive_passage).
- Использовать BM25 или Contriever для поиска по корпусу.
- Взять первый документ, который НЕ является релевантным — это и есть hard negative.
- Можно взять несколько (3–5) hard negatives на каждый запрос.
Важно, чтобы hard negatives были действительно сложными — например, документы из той же темы, но с другим ответом, или похожие по ключевым словам.
3. Mining: модель-ретривер даёт false positives
Процесс генерации hard negatives часто называют negative mining или hard negative mining. Основные подходы:
- BM25-based mining: BM25 выдаёт top-k, помечаем все, кроме релевантного, как hard negatives.
- Dense retriever mining (например, ANCE — Approximate Nearest Neighbor Negative Contrastive Estimation): в процессе обучения ретривера мы итеративно находим наиболее близкие векторы нерелевантных документов из индекса.
- Cross-encoder bootstrap: можно прогнать через уже обученный (или предобученный) cross-encoder партии (query, doc) и взять пары с высоким score, но не релевантные — это самые «жирные» hard negatives.
Для RAG обычно делают так:
# Псевдокод для mining hard negatives
queries = ["как ускорить LLM?"]
corpus = ["...", "...", "..."]
retriever = BM25(corpus)
hard_negatives = []
for q in queries:
top_k = retriever.retrieve(q, k=100)
for doc in top_k:
if not is_relevant(q, doc):
hard_negatives.append((q, doc, 0))
if len(hard_negatives) >= 3: break
4. Функция потерь: pairwise ranking loss
Наиболее распространённая функция для fine-tuning cross-encoder в ранжировании — pairwise ranking loss (также hinge loss или MarginRankingLoss).
Идея:
- Для каждого запроса берётся положительный документ (score=1) и один или несколько hard negatives (score=0).
- Модель обучается так, чтобы разница между оценками положительного и отрицательного документа была больше заданного маржина (margin).
Формула (для пары): [ \mathcal{L} = \max(0, margin - (s_positive - s_negative)) ]
В PyTorch это реализуется как:
import torch.nn as nn
criterion = nn.MarginRankingLoss(margin=0.3)
scores_pos = model(query, positive_doc) # [batch]
scores_neg = model(query, negative_doc) # [batch]
target = torch.ones_like(scores_pos) # +1 означает, что первый аргумент должен быть больше второго
loss = criterion(scores_pos, scores_neg, target)
Альтернативы:
- Listwise loss (ListNet, LambdaRank): учитывает порядок всего списка.
- Cross-entropy with multiple negatives: берём 1 positive + N negatives как классы, предсказываем софтмакс. Это эквивалентно pairwise при N=1, но с несколькими минусами даёт более стабильное обучение.
Рекомендации:
- Margin ~0.1–0.5.
- Для каждого запроса использовать 3–7 hard negatives (можно динамически подбирать сложность).
- Batch size 16–32, так как каждая пара требует отдельного forward.
5. Пет-проект для закрепления
Задача: Обучить cross-encoder ранжировать документы по запросу на датасете MS MARCO (или его подмножестве) с генерацией hard negatives через BM25.
Инструменты:
- Python 3.10+ с Hugging Face Transformers и PyTorch.
- Elasticsearch или pyserini для BM25 (можно локально).
- Датасет:
msmarco-passage(TREC 2019 Deep Learning Track). - Weights & Biases для логирования.
Шаги:
- Загрузить датасет MS MARCO (train queries + passages + qrels).
- Для каждого обучающего запроса отобрать top-50 документов через BM25.
- Выбрать оттуда 1 релевантный (по qrels) и 3 нерелевантных с наивысшим BM25-скорингом — это hard negatives.
- Взять предобученную модель
cross-encoder/ms-marco-MiniLM-L-6-v2(отличный старт). - Создать конкатенации (query, passage) с маской.
- Обучить с MarginRankingLoss (margin=0.2), batch size 16, learning rate 2e-5, 3 эпохи.
- Валидировать на dev-сете: метрика MRR@10 или nDCG@10.
Ожидаемый результат:
- После обучения модель будет давать более высокие оценки для релевантных документов, чем для hard negatives.
- На dev-сете MRR@10 вырастет на 5–15% по сравнению с предобученной версией (если fine-tuning сделан на домене).
- Понимание pipeline: retriever → reranker → final selection.
- Воспроизводимый код на GitHub (можно выложить в портфолио).
Связь с другими вопросами
| Вопрос | Тема |
|---|---|
| 45 | Этапы ретрива и ранжирования в RAG |
Навигация
- Предыдущий: 969
- Следующий: 971
- Индекс: 00. Индекс разборов