Дообучення Embedding-моделі під домен замовника
Стандартні embedding-моделі навчені на загальному веб-тексті та академічних корпусах. У спеціалізованих доменах (медицина, право, фінанси, технічні стандарти) вони погано розрізняють специфічну термінологію і показують зменшений retrieval recall. Fine-tuning embedding-моделі під конкретний домен підвищує якість пошуку без зміни всієї RAG-інфраструктури.
Коли необхідне дообучення embedding-моделі
Ознаки, які вказують на необхідність fine-tuning:
- Загальна embedding-модель погано розрізняє специфічні терміни домену (MeSH терміни, юридичні конструкції, технічні абревіатури)
- 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 тижні







