Если вы разрабатываете систему RAG (Retrieval-Augmented Generation), то вы, вероятно, столкнулись с этой проблемой: всё работает… пока не перестанет работать. Универсальные модели эмбеддингов обучены понимать интернет, а не ваши контракты, логи производства, пропиетарные химические формулы или внутреннюю таксономию. Они захватывают широкую семантическую схожесть, но не понимают тонких различий, которые имеют значение в вашей области. Дообучение модели эмбеддингов может улучшить производительность вашего конвейера поиска, когда готовые решения не справляются с захватом специфических для домена нюансов. Несмотря на критическую важность эмбеддингов для производительности RAG, этот процесс остаётся удивительно фрагментированным, требуемые навыки специализированы, а временные затраты значительны.
С одним GPU и менее чем за день тренировки вы можете превратить универсальную модель эмбеддингов в ту, что действительно понимает вашу область, без необходимости в ручной разметке данных. Чтобы помочь вам сразу начать, мы также выпускаем готовый к использованию синтетический набор обучающих данных, созданный на основе открытой документации NVIDIA с помощью этого самого конвейера. Используя эти данные и рецепт, мы добились улучшения более чем на 10% в Recall@10 и NDCG@10. Atlassian применила этот рецепт для дообучения на своём наборе данных JIRA, увеличив Recall@60 с 0.751 до 0.951, что составило 26% улучшение — на одном GPU.
🔗 Быстрые ссылки на набор данных и код:
- Модель эмбеддингов
- GitHub
- Синтетический набор данных на основе открытых документов NVIDIA
🧑💻 Проекты с открытым исходным кодом, интегрированные в рецепт:
- NeMo Data Designer для синтетического генерирования данных
- NeMo Automodel для обучения моделей эмбеддингов
- BEIR для оценки информационного поиска
- NeMo Export-Deploy для конвертации ONNX/TensorRT
- NVIDIA NIM для обслуживания вывода в production
📋 Предварительные требования:
- Директория с документами домена (текстовые файлы - .txt, .md или похожие)
- Валидный ключ API NVIDIA (бесплатно на build.nvidia.com)
- GPU NVIDIA Ampere или новее с минимум 80GB памяти (Compute Capability >= 8.0)
- Это руководство было протестировано на 1xA100 (80GB) и 1xH100 (80GB)
К концу этой статьи вы узнаете, как ответить на вопросы:
- 📄 Генерировать обучающие данные из документов домена без размеченных данных
- 🎯 Использовать mining жёстких негативов для эффективного контрастивного обучения
- 🔗 Улучшить качество эмбеддингов с помощью multi-hop запросов
- ⚙️ Дообучить модель эмбеддингов bi-encoder
- 📊 Оценить, улучшило ли дообучение качество поиска
- 🚀 Развернуть дообученную модель в вашем конвейере
⚙️ Подготовка
В этом руководстве мы будем дообучать базовую модель Llama-Nemotron-Embed-1B-v2 — модель эмбеддингов с 1 миллиардом параметров, которая балансирует качество и стоимость вывода. Чтобы начать работу, следуйте этому руководству по подготовке.
📚 Шаг 1: Генерируйте обучающие данные из документов
Дообучение модели эмбеддингов требует тысячи пар (запрос, релевантный документ). В большинстве случаев эти данные не доступны в готовом виде. Создание их вручную дорого, медленно и часто смещено личной интерпретацией аннотатора о том, что является "релевантным".
Вместо ручной разметки данных вы можете использовать LLM (nvidia/nemotron-3-nano-30b-a3b) для чтения ваших документов и автоматического генерирования высококачественных синтетических пар вопрос-ответ.
nemotron embed sdg -c default corpus_dir=./data/my_domain_docs
Как это работает?
За кулисами это запускает четырехэтапный конвейер синтетического генерирования данных (SDG), работающий на основе NeMo Data Designer:
Как выглядит результат?
Исходный кусок документа:
Тепловая мощность (TDP) GPU H100 составляет 700W в форм-факторе SXM. Система охлаждения должна поддерживать температуру кристалла ниже 83°C при длительных нагрузках. Жидкостное охлаждение рекомендуется для плотных развёртываний, превышающих 4 GPU на узел, поскольку воздушное охлаждение не может рассеять достаточно тепла в стандартных конфигурациях 2U.
Сгенерированные пары вопрос-ответ:
{
"question": "Какой подход охлаждения рекомендуется при развёртывании более 4 H100 GPU на одном узле сервера?",
"answer": "Жидкостное охлаждение рекомендуется для плотных развёртываний, превышающих 4 GPU на узел, поскольку воздушное охлаждение не может рассеять достаточно тепла в стандартных конфигурациях 2U.",
"query_type": "contextual",
"reasoning_type": "factual",
"question_complexity": 3,
"segment_ids": [1],
"quality_score": 8.5
}
{
"question": "Как 700W TDP H100 SXM ограничивает выбор между воздушным и жидкостным охлаждением в многоGPU конфигурациях?",
"answer": "700W TDP генерирует значительное тепло, которое должно рассеиваться для поддержания температуры кристалла ниже 83°C. В плотных конфигурациях, превышающих 4 GPU на узел, воздушное охлаждение в стандартном 2U шасси не может справиться с этой тепловой нагрузкой, что делает необходимым жидкостное охлаждение.",
"query_type": "multi_hop",
"reasoning_type": "causal",
"question_complexity": 4,
"segment_ids": [1, 2],
"hop_count": 2,
"quality_score": 9.0
}
Обратите внимание на разницу: первый вопрос — это простой поиск фактов. Второй требует многошагового, причинного рассуждения. Конвейер генерирует оба типа с настраиваемыми уровнями сложности (2–5) и количеством шагов (1–3). Каждая пара вопрос-ответ затем проходит оценку качества, получая под-оценки за релевантность, точность, поддержку контекстом и ясность, наряду с общей оценкой. В обучение включаются только пары, которые соответствуют порогу.
⛏️ Шаг 2: Mining жёстких негативов (и почему они имеют значение)
Если вы обучаете модель эмбеддингов только на позитивных парах (запрос + корректный документ), она учится различать очевидно разные документы, но терпит неудачу в сложных случаях — отрывках, которые выглядят релевантными, но не являются правильным ответом. В реальной системе поиска эти почти совпадения — это именно те документы, которые приводят к плохим ответам. Mining жёстких негативов находит эти запутанные отрывки, чтобы модель могла научиться их различать.
nemotron embed prep -c default
Вышеуказанная команда автоматически запускает три подэтапа:
2a. Разделение на тренировочный/валидационный/тестовый наборы
Сгенерированные пары вопрос-ответ разделяются на тренировочный (80%) и тестовый (20%) наборы. Тестовый набор форматируется как benchmark, совместимый с BEIR, для стандартизированной оценки на Шаге 5.
2b. Mining жёстких негативов
Используя базовую модель эмбеддингов, конвейер:
- Создаёт эмбеддинги каждого запроса и каждого отрывка в корпусе.
- Вычисляет подобие между каждым запросом и всеми отрывками.
- Маскирует помеченные позитивные документы каждого запроса.
- Применяет margin filter: любой не-позитивный документ, оценённый выше 95% от минимальной позитивной оценки, исключается. Эта зона исключения защищает от ложных негативов — неразмеченных отрывков, которые настолько близки к позитивным, что могут быть релевантны.
- Из выживших кандидатов выбирает top-k документов с наивысшей оценкой как жёсткие негативы (5 на запрос по умолчанию).
Результат: жёсткие негативы — это наиболее похожие не-позитивные отрывки, которые всё ещё безопасно находятся ниже потолка позитивной оценки. Это отрывки, которые текущая модель считает высоко релевантными, но которые не являются помеченным ответом.
Почему это работает: Обучение на лёгких негативах (полностью не связанных отрывках) ничему не учит модель. Обучение на жёстких негативах заставляет её учиться тонким различиям, которые имеют значение в вашей области. Например, в медицинском корпусе вопрос о "дозировке метформина при сахарном диабете 2 типа" может иметь жёсткие негативы о "побочных эффектах метформина" или "дозировке инсулина при сахарном диабете 1 типа" — близкие, но критически разные. Потолок 95% margin предотвращает выбор майнером отрывков, которые слишком близки к позитивному и могут быть на самом деле корректными ответами, которые просто не были помечены во время SDG.
2c. Разворачивание Multi-Hop
Multi-hop вопросы ссылаются на несколько позитивных документов. Например, вопрос вроде "Как система термального управления в Разделе 3.2 связана с ограничениями по мощности, описанными в Разделе 5.1?" имеет два позитивных отрывка.
Разворачивание создаёт один обучающий пример на каждую пару (запрос, позитивный документ), поэтому контрастивная потеря видит каждый позитив независимо. Вопрос с 2 позитивными документами становится 2 обучающими примерами, каждый с одинаковыми жёсткими негативами, но разным позитивом.
Финальный выход — это готовый к обучению JSON файл:
{
"question_id": "q42_0",
"question": "Как 700W TDP H100 SXM ограничивает выбор охлаждения в multi-GPU узлах?",
"pos_doc": [{"id": "d_a1b2c3"}],
"neg_doc": [{"id": "d_x7y8z9"}, {"id": "d_m4n5o6"}, {"id": "d_p1q2r3"}, {"id": "d_s4t5u6"}, {"id": "d_v7w8x9"}]
}
🔍 Шаг 3: Понимание Multi-Hop вопросов и почему они улучшают поиск
Стандартное дообучение эмбеддингов генерирует один вопрос на отрывок и учит модель их сопоставлять. Это работает для простых поиска фактов, но реальные пользователи задают сложные вопросы, охватывающие несколько документов или секций. Если модель видела только одношаговые обучающие данные, она будет бороться с поиском всех релевантных отрывков для этих сложных запросов.
Конвейер SDG генерирует вопросы с 1 по 3 шагов по умолчанию:
- 1-шаг: "Какой TDP у H100 SXM?" — ответ содержится в одном отрывке.
- 2-шага: "Как TDP H100 связана с требованиями охлаждения в плотных развёртываниях?" — требует соединения информации из двух отрывков.
- 3-шага: "Учитывая TDP, ограничения охлаждения и лимиты плотности в стойке, какое максимальное количество H100 GPU можно развернуть в стандартном ряду центра обработки данных?" — синтезирует три отрывка.
Каждый шаг отслеживается с его собственным резюме контекста и ID сегментов, поэтому обучающие данные сохраняют всю цепочку рассуждений. После разворачивания (Шаг 2c), каждая пара (вопрос, релевантный отрывок) становится независимым сигналом обучения, обучая модель тому, что все эти отрывки релевантны для multi-hop запроса.
Дообученная модель учится извлекать контекстно связанные документы, а не только лексически похожие.
🧠 Шаг 4: Дообучение модели эмбеддингов
nemotron embed finetune -c default
Как работает контрастивное обучение
Обучение использует архитектуру biencoder с контрастивной потерей.
Температура 0.02 намеренно агрессивна, производя очень острое распределение вероятностей. Это работает хорошо, потому что жёсткие негативы из Шага 2 высокого качества: это действительно запутанные отрывки, для различения которых модели нужны сильные градиенты.
Ключевые гиперпараметры
| Параметр | По умолчанию | Примечания |
|---|---|---|
| Эпохи | 3 | Для больших наборов данных вы можете снизить это до 2 или 1 |
| Скорость обучения | 1e-5 | Настройка: попробуйте двойное и половину значения по умолчанию |
| Шаги разогрева скорости обучения | 5 | Установите на 5-10% от общего количества шагов дообучения для лучшей стабильности на раннем этапе |
| Глобальный размер батча | 128 | Автоматически масштабируется вниз для малых наборов данных |
| Отрывки на запрос | 5 | 1 позитив + 4 жёстких негатива |
Автоматическое масштабирование для малых наборов данных
Если ваш набор данных содержит менее 2 000 примеров для обучения, конвейер автоматически:
- Снижает размер батча (до 16–64), чтобы градиенты были значимыми.
- Регулирует частоту чекпоинтов для обеспечения минимум трёх чекпоинтов на запуск.
- Масштабирует частоту валидации пропорционально.
Это означает, что вы можете начать с небольшого корпуса (50–100 документов) для быстрого proof-of-concept и масштабировать позже.
📈 Шаг 5: Измеряйте улучшение
Действительно ли дообучение помогло? Давайте узнаем, запустив стандартизированную оценку, сравнивающую базовую модель с дообученной контрольной точкой на удержанном тестовом наборе:
nemotron embed eval -c default
Оценка использует фреймворк BEIR и вычисляет четыре стандартных метрики информационного поиска при k = 1, 5, 10 и 100:
- nDCG@k: Качество ранжирования — лучшие документы ранжируются выше?
- Recall@k: Покрытие — какая доля релевантных документов появляется в top k?
- Precision@k: Точность — какая доля top k результатов действительно релевантна?
- MAP@k: Средняя точность для всех запросов
Успешное дообучение обычно приводит к 15% улучшению в nDCG@10 и Recall@10 менее чем за 1 день.
Результаты с использованием Retrieval Synthetic NVDocs:
📊 Сравнение (Базовая -> Дообученная)
============================================================
NDCG:
NDCG@1: 0.55178 → 0.60796 (+0.05618, +10.2%)
NDCG@5: 0.51894 → 0.57689 (+0.05795, +11.2%)
NDCG@10: 0.55506 → 0.61559 (+0.06053, +10.9%)
NDCG@100: 0.60617 → 0.66567 (+0.05950, +9.8%)
Recall:
Recall@1: 0.28478 → 0.31547 (+0.03069, +10.8%)
Recall@5: 0.54486 → 0.60288 (+0.05802, +10.6%)
Recall@10: 0.62979 → 0.69296 (+0.06317, +10.0%)
Recall@100: 0.81421 → 0.87020 (+0.05599, +6.9%)
Что если числа не улучшаются?
Конвейер облегчает итерирование:
- Низкие оценки качества в SDG? Проверьте качество ваших документов — чистый, хорошо отформатированный текст производит лучшие синтетические данные. Попробуйте большую и более мощную LLM.
- Недостаточно обучающих данных? Добавьте больше документов в ваш корпус и перезапустите Stage 0.
- Переобучение? Снизьте эпохи или увеличьте порог качества, чтобы оставить только лучшие примеры для обучения.
- Неправильная скорость обучения? Попробуйте 5e-6 для больших наборов данных или 2e-5 для очень малых.
🏆 Результаты из реального мира: Atlassian
Этот рецепт был проверен на реальных корпоративных данных компанией Atlassian. Они применили этот конвейер для дообучения Llama-Nemotron-Embed-1B-v2 на общедоступном наборе данных Jira, используя один GPU NVIDIA A100 80GB, следуя тем же этапам, описанным выше.
Recall@60 прыгнул с 0.751 до 0.951 — прирост на 26.7%.
Дообученная модель извлекает корректный документ в top 60 результатов для 95.1% запросов, вверх с 75.1% с базовой моделью.