Дообучение Embedding-модели под домен заказчика

Проектируем и внедряем системы искусственного интеллекта: от прототипа до production-ready решения. Наша команда объединяет экспертизу в машинном обучении, дата-инжиниринге и MLOps, чтобы AI работал не в лаборатории, а в реальном бизнесе.
Показано 1 из 1Все 1566 услуг
Дообучение Embedding-модели под домен заказчика
Средний
от 1 недели до 3 месяцев
Часто задаваемые вопросы

Направления AI-разработки

Этапы разработки AI-решения

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1284
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1196
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    901
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1119
  • image_logo-advance_0.webp
    Разработка логотипа компании B2B Advance
    586
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    853

Дообучение Embedding-модели под домен заказчика

Стандартные embedding-модели обучены на общем веб-тексте и академических корпусах. В специализированных доменах (медицина, право, финансы, технические стандарты) они плохо различают специфическую терминологию и показывают сниженный retrieval recall. Fine-tuning embedding-модели под конкретный домен повышает качество поиска без смены всей RAG-инфраструктуры.

Когда необходимо дообучение embedding-модели

Признаки, указывающие на необходимость fine-tuning:

  • Общая embedding-модель плохо различает специфические термины домена (MeSH terms, юридические конструкции, технические аббревиатуры)
  • Context recall RAG-системы застрял ниже 0.75 несмотря на оптимизацию чанкинга и поиска
  • Высокая доля «ложных срабатываний» — семантически похожие, но тематически нерелевантные документы попадают в top-K

Подготовка обучающих данных: Triplet vs Contrastive

Triplet Loss: якорь (anchor), позитив (релевантный документ), негатив (нерелевантный).

Contrastive / Multi Negative Ranking Loss: пары (запрос, релевантный документ). Негативы берутся из остальных документов в батче.

from sentence_transformers import SentenceTransformer, InputExample, losses
from torch.utils.data import DataLoader

# Обучающие данные: пары (query, relevant_document)
train_examples = [
    InputExample(texts=["каков срок исковой давности по трудовым спорам",
                        "Срок исковой давности по индивидуальным трудовым спорам составляет 3 месяца..."]),
    InputExample(texts=["расчёт пособия по временной нетрудоспособности",
                        "Размер пособия по временной нетрудоспособности определяется..."]),
    # ... ещё примеры
]

# Загрузка базовой модели
model = SentenceTransformer("BAAI/bge-m3")

# Multi Negative Ranking Loss (эффективнее triplet для retrieval)
train_dataloader = DataLoader(train_examples, shuffle=True, batch_size=32)
train_loss = losses.MultipleNegativesRankingLoss(model)

# Обучение
model.fit(
    train_objectives=[(train_dataloader, train_loss)],
    epochs=3,
    warmup_steps=100,
    output_path="./bge-m3-legal-finetuned",
    show_progress_bar=True,
)

Генерация обучающих пар с LLM

Для создания обучающего датасета без ручной разметки:

from openai import OpenAI
import json

client = OpenAI()

def generate_queries_for_document(doc_text: str, n: int = 5) -> list[str]:
    """Генерирует поисковые запросы, на которые отвечает документ"""
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{
            "role": "user",
            "content": f"""Сгенерируй {n} поисковых запросов, которые пользователь мог бы задать,
чтобы найти следующий документ. Запросы должны быть разными по формулировке,
но все должны быть отвечены этим документом.
Верни JSON-список строк.

Документ:
{doc_text[:1000]}"""
        }],
        response_format={"type": "json_object"},
    )
    return json.loads(response.choices[0].message.content)["queries"]

# Генерация датасета из 1000 документов
training_pairs = []
for doc in domain_documents:
    queries = generate_queries_for_document(doc.text)
    for query in queries:
        training_pairs.append(InputExample(texts=[query, doc.text]))

print(f"Сгенерировано {len(training_pairs)} обучающих пар")
# Типично: 1000 документов × 5 запросов = 5000 пар за ~2 часа и $5-15

Оценка улучшения retrieval

from sentence_transformers.evaluation import InformationRetrievalEvaluator

# Тестовый набор: {query_id: query, corpus_id: doc, relevant_docs: {query_id: [doc_ids]}}
evaluator = InformationRetrievalEvaluator(
    queries=test_queries,
    corpus=test_corpus,
    relevant_docs=relevance_labels,
    precision_recall_at_k=[1, 5, 10],
    ndcg_at_k=[10],
    show_progress_bar=True,
)

# Сравнение базовой и дообученной модели
base_model = SentenceTransformer("BAAI/bge-m3")
finetuned_model = SentenceTransformer("./bge-m3-legal-finetuned")

base_results = evaluator(base_model, output_path="./eval_base")
ft_results = evaluator(finetuned_model, output_path="./eval_finetuned")

Практический кейс: юридические документы

Базовая модель: BAAI/bge-m3. Датасет: 8000 пар (синтетических: 6500 через GPT-4o-mini + 1500 ручных). Домен: российское трудовое и гражданское право.

Метрика До FT После FT
NDCG@10 0.68 0.84
Recall@5 0.61 0.79
MRR@5 0.65 0.82
Latency (inference) без изменений без изменений

+24% к NDCG без изменения инфраструктуры — только обновление весов модели.

Сроки

  • Генерация обучающего датасета: 3–7 дней
  • Fine-tuning (bge-m3, 5000 пар): 2–4 часа (A100)
  • Evaluation и сравнение: 2–3 дня
  • Итого: 1–2 недели