中文翻译暂不可用,显示俄语原文。
RAG с cross-encoder reranking
ТЕХНИЧЕСКОЕ ЗАДАНИЕ: RAG с cross-encoder reranking
1. Цель задачи
Реализовать пайплайн двухэтапного retrieval для RAG: сначала ANN (приблизительный поиск ближайших соседей) возвращает top-100 кандидатов, затем cross-encoder переранжирует их в top-10. Измерить качество ранжирования метрикой NDCG@10 и добиться её улучшения на 30% относительно baseline (только ANN, без реранжирования). Задача развивает навыки интеграции эффективного поиска с точным реранжированием и оценки ранжирующих систем.
Ключевой результат Пайплайн ANN → cross-encoder → top-10 с фиксированным датасетом, где NDCG@10 улучшен на ≥30% по сравнению с baseline.
2. Исходные данные
| Что нужно | Откуда взять |
|---|---|
| Датасет: запросы + документы + релевантность (qrels) | MS MARCO Passage Ranking (dev queries + corpus) или TREC Robust04; скачать с Hugging Face Datasets |
| Embedding-модель для ANN (bi-encoder) | sentence-transformers/all-MiniLM-L6-v2 (лёгкая, хорошее качество) |
| Cross-encoder модель | cross-encoder/ms-marco-MiniLM-L-6-v2 (подходит для ранжирования) |
| Векторный индекс для ANN | FAISS index (FlatIP для точности, или IVF для скорости); построить из эмбеддингов документов |
| Baseline без re-ranker | Просто top-10 по ANN, те же метрики |
Если нет реального датасета — симулируем:
- Используем
datasets.load_dataset("ms_marco", "passage_ranking")— это dev queries (6980 запросов) и пассажи с ID. - Если qrels не хватает, возьмём первые 1000 запросов и вручную не надо — qrels уже есть в датасете.
- Если нет GPU для cross-encoder, используем меньшую модель:
cross-encoder/stsb-distilroberta-base(но лучше ms-marco) или batch inference на CPU (медленно, но допустимо для небольшой выборки). Для оценки NDCG достаточно прогнать 200–500 запросов.
3. Технологический стек
| Компонент | Инструменты | Назначение |
|---|---|---|
| Язык | Python 3.10+ | Весь код |
| Векторный поиск | FAISS (faiss-cpu) | Построение индекса и ANN search (top-100) |
| Bi-encoder эмбеддинги | sentence-transformers | Вычисление эмбеддингов документов и запросов |
| Cross-encoder | sentence-transformers (CrossEncoder класс) | Реранжирование кандидатов (top-100 → top-10) |
| Метрики | sklearn.metrics.ndcg_score, ir-measures | NDCG@10, Recall@10, MRR |
| Датасет | datasets (Hugging Face) | MS MARCO или TREC |
| Эксперименты | Jupyter Notebook или .py скрипт | Отладка и фиксация результатов |
| Версионирование | Git + DVC (опционально) | Код и данные |
4. Этапы выполнения
Этап 1: Подготовка данных и baseline (1 час)
Действия
- Загрузить датасет MS MARCO passage ranking:
from datasets import load_dataset dataset = load_dataset("ms_marco", "passage_ranking", split="dev") queries = dataset.select(range(1000)) # уменьшить для скорости - Извлечь ID запросов, текст запросов, ID релевантных документов (qrels).
- Загрузить корпус пассажей (corpus) — отдельный датасет
ms_marco.passages. - Построить эмбеддинги пассажей с bi-encoder:
from sentence_transformers import SentenceTransformer bi_encoder = SentenceTransformer('all-MiniLM-L6-v2') corpus_texts = [passage['text'] for passage in corpus] corpus_embeddings = bi_encoder.encode(corpus_texts, convert_to_numpy=True, show_progress_bar=True) - Построить FAISS индекс (FlatIP — внутреннее произведение, т.е. cosine после нормализации):
import faiss index = faiss.IndexFlatIP(corpus_embeddings.shape[1]) faiss.normalize_L2(corpus_embeddings) index.add(corpus_embeddings) - Baseline retrieval для каждого запроса получить top-10 по ANN. Для этого нормализовать эмбеддинг запроса (L2) и выполнить
index.search. - Вычислить NDCG@10 baseline:
from sklearn.metrics import ndcg_score # подготовка релевантностей: 1 для релевантных, 0 для нерелевантных results_baseline = [] for qid, q_emb in zip(query_ids, query_embeddings): distances, indices = index.search(q_emb[np.newaxis,:], 10) doc_ids = [corpus_ids[i] for i in indices[0]] # получить метки релевантности y_true = [1 if did in qrels[qid] else 0 for did in doc_ids] y_score = [1 - distances[0][i] for i in range(10)] # чем больше distance, тем ближе; инвертируем ndcg_baseline.append(ndcg_score([y_true], [y_score], k=10)) print("Baseline NDCG@10:", np.mean(ndcg_baseline))
Ожидаемый результат этапа Файл baseline_metrics.json со средним NDCG@10 и Recall@10.
Этап 2: Реализация re-ranker (cross-encoder) (1.5 часа)
Действия
- Загрузить cross-encoder модель:
from sentence_transformers import CrossEncoder model_ce = CrossEncoder('cross-encoder/ms-marco-MiniLM-L-6-v2', max_length=512) - Для каждого запроса получить top-100 кандидатов через ANN (из FAISS).
- Записать пары (запрос, пассаж) для всех кандидатов.
- Внимание: не перепутать нормализацию; ANN должен вернуть 100 кандидатов.
- Прогнать cross-encoder на парах (query, passage) батчами (batch_size=32–64) для ускорения.
- Отсортировать кандидаты по убыванию скоров cross-encoder, взять top-10.
- Вычислить NDCG@10 для re-ranked результатов.
Ожидаемый результат этапа Файл reranked_metrics.json с метриками.
Этап 3: Сравнение и анализ (1 час)
Действия
- Сравнить средний NDCG@10 baseline и re-ranked.
- Вычислить относительное улучшение:
(ndcg_rerank - ndcg_baseline) / ndcg_baseline * 100. - Построить график распределения NDCG@10 по запросам (гистограмма или boxplot).
- Проанализировать случаи, где реранжирование ухудшило результат (если есть). Возможные причины: шум в qrels, неудачная модель cross-encoder.
- Зафиксировать дополнительно Recall@10, MRR@10 (для полноты).
Ожидаемый результат этапа Отчёт comparison_report.md с таблицами метрик и графиками.
Этап 4: Оптимизация (опционально, 1 час – при желании улучшить результат)
Действия
- Попробовать другой cross-encoder:
cross-encoder/ms-marco-TinyBERT-L-2(быстрее) илиBAAI/bge-reranker-v2-m3(если доступен). - Увеличить число ANN кандидатов (top-200 вместо 100) — может дать прирост, но замедлит.
- Применить hybrid: weighted sum ANN score + cross-encoder score.
- Повторить этап 2 и 3, записать улучшения.
Ожидаемый результат этапа Файл optimized_metrics.json с лучшим результатом.
Этап 5: Фиксация и рефакторинг (0.5 часа)
Действия
- Структурировать код в модули:
embed.py,search.py,rerank.py,evaluate.py. - Написать
main.py, который запускает весь пайплайн с параметрами (bi-encoder, cross-encoder, top_k). - Добавить README.md с инструкцией по запуску и результатами.
- Закоммитить в Git.
Ожидаемый результат этапа Чистый репозиторий с воспроизводимым пайплайном.
5. Критерии приемки (Definition of Done)
- Датасет загружен и обработан (корпус, запросы, qrels).
- Реализован baseline retrieval (ANN → top-10) с измеренным NDCG@10.
- Реализован re-ranking пайплайн (ANN top-100 → cross-encoder → top-10).
- NDCG@10 после реранжирования улучшен на ≥30% относительно baseline.
- Все метрики зафиксированы в JSON-файлах.
- Код воспроизводим: однократный запуск
python main.pyвыдаёт метрики. - README.md содержит описание, команды запуска и итоговые метрики.
- Отчёт с визуализацией (гистограмма распределения NDCG) присутствует.
- Код залит в репозиторий с хотя бы одним коммитом.
6. Ожидаемый результат
- Репозиторий (папка проекта) с файлами:
main.py— точка входа.embed.py— функции построения эмбеддингов и FAISS индекса.search.py— ANN retrieval.rerank.py— cross-encoder re-ranking.evaluate.py— вычисление NDCG, Recall, MRR.requirements.txt— зависимости.README.md— описание, результаты, команды запуска.baseline_metrics.json,reranked_metrics.json— JSON с метриками.comparison_report.md— отчёт с таблицами и графиками.
- Метрики:
- Опционально файл с визуализацией (PNG/PDF).
7. Возможные сложности и их решение
| Сложность | Решение |
|---|---|
| Датасет MS MARCO большой (8.8 млн пассажей) | Использовать подмножество: первые 100 000 пассажей, 500 запросов. Для ANN index использовать FlatIP (точность) или IVF (скорость) — при малом размере Flat нормально. |
| Cross-encoder медленный (CPU) | Использовать batch inference (batch_size=32); уменьшить количество запросов до 100; взять меньшую модель (TinyBERT). |
| NDCG@10 не улучшается на 30% | 1) Проверить baseline: ANN может быть уже хорош. 2) Убедиться, что qrels полные (в MS MARCO qrels есть только 1 релевантный пассаж на запрос). 3) Увеличить количество кандидатов (top-200). 4) Взять более сильный cross-encoder (BAAI/bge-reranker-v2-m3). |
| Ошибки нормализации для ANN (cosine) | Используем faiss.IndexFlatIP + faiss.normalize_L2 для всех эмбеддингов. Query тоже нормализуется. |
| Дубликаты ID пассажей | Использовать уникальные ID из датасета (passage_id). |
8. Бюджет времени (оценка)
| Этап | Время (часы) |
|---|---|
| 1. Подготовка данных и baseline | 1.0 |
| 2. Реализация re-ranker | 1.5 |
| 3. Сравнение и анализ | 1.0 |
| 4. Оптимизация (опционально) | 1.0 |
| 5. Фиксация и рефакторинг | 0.5 |
| Итого | 5.0 |
Примечание Для первого раза ожидаемый диапазон 4–6 часов в зависимости от опыта и производительности машины.
9. Связанные вопросы из базы знаний
| Вопрос | Тема |
|---|---|
| 12 | Какие метрики качества retrieval (hit rate, MRR, NDCG) лучше всего подходят для оценки RAG? |
| 47 | Как реализовать ANN с FAISS на больших корпусах? |
| 88 | Чем bi-encoder отличается от cross-encoder в контексте retrieval? |
| 145 | Как нормализовать эмбеддин |