Перейти к содержанию
Публикация AiManual

Как дообучить reranker-модель под свои задачи: полный гайд по Sentence Transformers

Практический гайд по дообучению reranker и cross-encoder в Sentence Transformers. Разбираем датасеты, hard negatives, CrossEncoderLoss, MultipleNegativesRanking

Коротко

Что будет в материале

  1. 01

    Как дообучить reranker-модель: краткий ответ

  2. 02

    Что такое reranker и зачем ему дообучение

  3. 03

    Подготовка датасета для обучения reranker

  4. 04

    Hard negatives: почему случайных негативов недостаточно

Reranker-модель дообучают на примерах вида query плюс документ, где для каждого результата известна релевантность. Рабочий пайплайн включает сбор разметки, майнинг hard negatives, выбор loss-функции, разделение данных на train, validation и test, запуск тренера Sentence Transformers и сравнение с baseline.

Для поиска и RAG обычно используют cross-encoder: retriever быстро отбирает небольшой пул кандидатов, а reranker заново оценивает каждую пару «запрос - документ». Качество данных и сложность негативных примеров часто влияют на результат сильнее, чем увеличение размера базовой модели. Кейс с 99 тысячами пар и обучением около 30 минут показывает возможный результат конкретного эксперимента, но не дает универсальной гарантии по скорости или качеству.

Главная практическая идея проста: модель должна видеть ошибки, которые действительно возникают в вашем поисковом контуре. Случайный нерелевантный текст учит отличать очевидное. Hard negative, похожий на правильный документ, заставляет модель разбираться в намерении запроса, терминах, версиях и контексте.

Как дообучить reranker-модель: краткий ответ

Последовательность действий выглядит так:

  1. Соберите запросы и документы из поисковых логов, RAG-пайплайна или ручной разметки.
  2. Для каждого query определите один или несколько positive, которые действительно отвечают на запрос.
  3. Получите кандидатов от retriever и выберите среди них проверенные hard negative.
  4. Свяжите структуру датасета с loss-функцией: бинарная оценка, числовой score, парное сравнение или контрастивное обучение требуют разного входного формата.
  5. Разделите данные на train, validation и test, убрав дубли и связанные запросы из разных выборок.
  6. Загрузите готовый cross-encoder либо создайте модель на базе предварительно обученного encoder, настройте тренер Sentence Transformers и сохраните checkpoint.
  7. Сравните новую модель с исходной на одинаковом пуле кандидатов и разберите ошибки вручную.

Начинайте с готовой модели, если у вас есть ограниченный объем разметки. Обучение с нуля требует больше данных, вычислений и контроля стабильности. Для первого эксперимента важнее получить чистый test-набор и понятный baseline, чем сразу перебирать десятки гиперпараметров.

Что такое reranker и зачем ему дообучение

В двухэтапном поиске retriever превращает запрос в вектор или использует быстрый лексический индекс, после чего возвращает десятки или сотни кандидатов. Reranker получает исходный запрос вместе с текстом каждого кандидата и вычисляет более точную оценку релевантности. Затем система сортирует документы по этим оценкам.

Такая схема подходит для RAG: retriever расширяет охват поиска, а cross-encoder сокращает число неподходящих фрагментов перед передачей контекста языковой модели. Методы оценки качества RAG и контроль ошибок в полном пайплайне разобраны в отдельном руководстве по измерению качества RAG-систем.

Bi-encoder и cross-encoder: в чем практическая разница

СвойствоBi-encoderCross-encoder
Обработка запроса и документаКодирует их отдельноОбрабатывает пару совместно
Поиск по большой коллекцииПодходит для быстрого поиска векторовСлишком дорог для полного перебора
Учет взаимодействия токеновОграничен отдельными эмбеддингамиМодель видит связи между запросом и документом
Типичная рольПервичный retrieverПерестановка верхних кандидатов

Bi-encoder заранее строит эмбеддинги документов. При новом запросе система сравнивает один вектор с индексом, поэтому такой подход масштабируется на большие коллекции. Cross-encoder читает каждую пару отдельно и обычно дает более точную оценку, но его стоимость растет вместе с количеством кандидатов и длиной текста.

Для практического контура часто выбирают 20-100 кандидатов, однако точное число зависит от задержки, размера модели и требований к полноте поиска. Reranker не заменяет retriever: если правильного документа нет среди кандидатов, перестановка результатов его не восстановит.

Когда достаточно готовой модели, а когда нужен fine-tune cross encoder

Готового reranker достаточно, когда запросы и документы близки к данным, на которых обучали базовую модель, а ошибки не имеют устойчивого доменного характера. Такой вариант экономит время на разметку и дает быстрый baseline.

Дообучение оправдано, если система регулярно путает похожие термины, внутренние названия, версии документов или типы пользовательского намерения. Причиной для fine-tune служит и собственная шкала релевантности, например разделение между «точно отвечает», «частично отвечает» и «не отвечает».

Нужны реальные примеры ошибок. Без них fine-tune легко превращается в подгонку под случайные метки. После обучения придется измерять задержку инференса: более длинный вход или больший пул кандидатов способен свести прирост качества на нет.

Подготовка датасета для обучения reranker

Минимальные сущности датасета: query, positive и negative. Запрос должен отражать формулировку пользователя, документ должен иметь тот же вид, в котором его увидит reranker, включая заголовок, метаданные и текстовый фрагмент.

Если в production документы режутся на чанки, обучайте модель на чанках или на формате, максимально близком к нему. Иначе модель будет оценивать одну структуру текста, а работать с другой.

Какие форматы примеров поддерживает Sentence Transformers

ФорматПримерПодходящий сигнал
Пара с меткойquery, document, labelБинарная или числовая релевантность
Тройкаquery, positive, negativeСравнение правильного и неправильного кандидата
Несколько кандидатовОдин запрос и список документов с оценкамиРанжирование внутри группы
Пары для контрастивного обученияquery, positiveIn-batch negatives в выбранной loss-функции

Названия столбцов можно адаптировать, но смысл полей должен оставаться однозначным. Для CrossEncoderLoss в режиме оценки пары обычно нужна пара текстов и целевая метка. Для MultipleNegativesRankingLoss нужны положительные пары, а остальные positive в батче автоматически выступают негативами для текущего запроса.

Тройка с hard negative подходит для схем, где функция потерь явно сравнивает positive и negative. Подставлять один и тот же DataFrame во все режимы нельзя: тренер ожидает конкретные поля и форму примера.

Как определить positive и negative без двусмысленной разметки

Positive должен отвечать на конкретный запрос, а не просто содержать похожие слова. Документ, который редактор еще не проверил, нельзя автоматически считать negative. Это неизвестный пример, а не доказанно нерелевантный.

Заранее зафиксируйте правила:

  • точный ответ на вопрос получает положительную метку;
  • частичный ответ получает отдельную оценку либо исключается из бинарного набора;
  • устаревший документ помечается с учетом версии и даты;
  • противоречивые или спорные примеры попадают в отдельную очередь проверки;
  • дубли одного документа не распределяются между positive и negative.

Числовая шкала должна иметь устойчивый смысл. Если значение 1 означает «релевантно», а 0 означает «не проверено», модель получает смешанный сигнал. В таком случае лучше удалить сомнительные записи или разметить их отдельно.

Разделение train, validation и test

Train нужен для обновления весов. Validation помогает выбирать эпохи, learning rate и другие параметры. Test остается нетронутым до финального сравнения.

Разделяйте данные по группам, а не случайно по отдельным строкам, если несколько запросов относятся к одной теме или используют один документ. Дедупликация должна учитывать нормализованный текст, идентификатор документа, версию и близкие формулировки запроса.

Утечка возникает, когда почти одинаковый запрос оказывается в train и test или когда один и тот же документ представлен в обеих выборках с незначительными изменениями. Такая оценка выглядит лучше реальной эксплуатации.

Hard negatives: почему случайных негативов недостаточно

Hard negative похож на правильный документ по лексике или смыслу, но не решает конкретную задачу пользователя. Например, запрос касается установки драйвера для одной версии GPU, а кандидат описывает похожую процедуру для другой версии. Случайный текст про несвязанную тему даст модели слишком простой пример.

Качество hard negatives часто определяет, научится ли reranker различать тонкие признаки релевантности. Ошибка в обратную сторону опаснее: похожий negative может оказаться скрытым positive и испортить разметку.

Как майнить hard negatives из результатов retriever

  1. Возьмите набор запросов с подтвержденными positive.
  2. Запустите текущий retriever и сохраните верхние кандидаты, например первые 20-100 документов.
  3. Удалите positive, их дубли и документы, которые редактор еще не успел проверить.
  4. Выберите кандидатов с высокой близостью к запросу, но с подтвержденной нерелевантностью.
  5. Сохраните retriever, его параметры и позицию кандидата, чтобы позднее анализировать источник ошибки.

Майнинг должен повторять реальный путь данных. Если production использует конкретный embedder, фильтры и размер top-k, собирайте hard negatives после этих этапов. Иначе модель будет учиться на ошибках, которые пользователи не видят.

Для крупных наборов можно запускать майнинг итерациями: сначала обучить базовый reranker, затем найти документы, которые он переоценивает, проверить разметку и добавить новые примеры. Каждая итерация должна сохранять отдельную версию датасета.

Какие негативы ухудшают обучение

  • Очевидно нерелевантные документы, которые не требуют сравнения.
  • Дубли positive, ошибочно переименованные в negative.
  • Фрагменты с потерянным заголовком или контекстом, когда их релевантность невозможно определить.
  • Спорные документы, для которых у разметчиков нет общего правила.
  • Устаревшие версии, если пользовательский запрос допускает несколько корректных вариантов.

Количество негативов не компенсирует плохую проверку. Лучше иметь меньше примеров с понятной причиной нерелевантности, чем большой набор шумных записей.

Hard negatives для доменного дообучения

В узком домене сложные негативы должны отражать реальные различия: похожие названия продуктов, близкие артикулы, разные версии API, документы для разных ролей и одинаковые термины с разным намерением.

Для RAG по внутренней базе полезно сохранять источник, тип документа и дату версии. Эти поля помогают понять, почему кандидат ошибочен, но передавать их модели нужно только тогда, когда они присутствуют в production-запросе.

Выбор loss-функции под задачу и формат данных

Loss-функция определяет, какой сигнал получает модель. Одни режимы учат предсказывать абсолютную оценку пары, другие заставляют ставить positive выше negative, третьи формируют контрастивное сравнение внутри батча.

CrossEncoderLoss для оценки релевантности пары

В базовом сценарии модель получает пару (query, document) и возвращает score. Для бинарной разметки используют метки вроде 0 и 1. Для более детальной оценки можно применять согласованную числовую шкалу, если выбранная реализация loss поддерживает такой режим.

Пример структуры:

query,document,label
как установить драйвер,Инструкция для версии X,1
как установить драйвер,Описание версии Y,0

Названия конкретных классов loss зависят от версии Sentence Transformers и выбранного тренера. Проверяйте сигнатуру установленной версии перед запуском. Смысл остается тем же: метка должна соответствовать диапазону и интерпретации, которые ожидает функция.

MultipleNegativesRankingLoss и обучение на сравнении кандидатов

MultipleNegativesRankingLoss работает с положительными парами. Внутри батча positive другого запроса становятся негативами для текущего запроса. При размере батча B каждый запрос получает до B - 1 дополнительных сравнений, если исключить собственную положительную пару.

Схема эффективна, когда запросы в одном батче не имеют общих positive и не относятся к настолько близким темам, что positive одного запроса подходит другому. Иначе появляются ложные негативы. Hard negatives можно подключать отдельным режимом или использовать loss, которая принимает явные тройки, если это предусмотрено вашей версией библиотеки.

Для cross-encoder важно отличать контрастивное обучение эмбеддера от обучения модели, которая непосредственно выдает оценку пары. Одинаковое название «ranking» не означает одинаковый формат входных данных.

Как выбрать loss-функцию без гадания

  1. Определите разметку: бинарная, числовая, парная или групповая.
  2. Проверьте формат, который принимает loss и выбранный тренер.
  3. Подготовьте небольшой чистый validation-набор с теми же правилами.
  4. Запустите один-два сопоставимых эксперимента.
  5. Выберите вариант по метрикам и стабильности, а не по минимальному training loss.

Датасет проектируют вместе с loss-функцией. Если основной сигнал состоит в различии между двумя близкими документами, пары с абсолютными метками могут скрыть нужную структуру. Если positive для разных запросов пересекаются, in-batch схема требует дополнительной проверки.

Запуск обучения в Sentence Transformers

Дообучение готового cross-encoder или обучение с нуля

Дообучение предварительно обученного cross-encoder обычно требует меньше данных и времени. Базовые веса уже кодируют общие языковые закономерности, поэтому датасет может сосредоточиться на терминологии и правилах конкретного домена.

Обучение с нуля имеет смысл при особом языке, закрытой предметной области или отсутствии подходящей базовой модели. Потребуются большие объемы качественных пар, корректная токенизация и дополнительные проверки на переобучение. Маленькая ошибка в разметке при таком сценарии способна заметно изменить поведение модели.

Типичный каркас для cross-encoder выглядит так:

from datasets import load_dataset
from sentence_transformers import CrossEncoder
from sentence_transformers.cross_encoder import CrossEncoderTrainer
from sentence_transformers.cross_encoder import CrossEncoderTrainingArguments

model = CrossEncoder("base-cross-encoder")
dataset = load_dataset("csv", data_files={
    "train": "train.csv",
    "validation": "validation.csv"
})

args = CrossEncoderTrainingArguments(
    output_dir="./reranker-checkpoints",
    num_train_epochs=2,
    per_device_train_batch_size=16,
    learning_rate=2e-5,
    eval_strategy="steps",
    save_strategy="steps",
    load_best_model_at_end=True,
    fp16=True
)

trainer = CrossEncoderTrainer(
    model=model,
    args=args,
    train_dataset=dataset["train"],
    eval_dataset=dataset["validation"]
)
trainer.train()
model.save_pretrained("./reranker-final")

Это каркас для режима, где структура CSV и loss совпадают с ожиданиями установленной версии API. В реальном проекте явно задайте преобразование полей и подключите подходящую loss-функцию, если тренер не выбирает ее автоматически. Имя базовой модели в примере условное, его нужно заменить на доступный checkpoint.

Ключевые аргументы тренера

ПараметрНа что влияетРиск
batch sizeПамять, скорость и число сравнений в батчеНедостаток памяти или ложные in-batch negatives
learning rateСкорость изменения весовНестабильное обучение или слабая адаптация
num_train_epochsЧисло проходов по trainПереобучение на небольшом доменном наборе
max sequence lengthМаксимальную длину входаОбрезание ответа или рост задержки и VRAM
eval strategyЧастоту проверки validationПозднее обнаружение деградации
save strategyЧастоту сохранения checkpointПотерю лучшей версии при сбое
fp16 или bf16Потребление памяти и скорость на совместимом GPUПроблемы стабильности или отсутствие поддержки

Начните с умеренного batch size и подберите learning rate по validation. Увеличение числа эпох не гарантирует улучшение. Сохраняйте лучший checkpoint по целевой метрике, если тренер и evaluator поддерживают такой режим.

Память GPU, скорость и воспроизводимость

Память растет вместе с длиной запроса, длиной документа, размером батча и внутренними активациями трансформера. Сокращение текста до полезного контекста часто дает больший эффект, чем агрессивное уменьшение batch size.

При нехватке VRAM уменьшайте длину входа и batch size, используйте gradient accumulation и mixed precision при поддержке оборудования. Изменяйте один параметр за раз, иначе причина изменения метрики останется неизвестной.

Фиксируйте версии Python, Sentence Transformers, Transformers и PyTorch, seed, конфигурацию токенизатора, параметры майнинга и хеш версии датасета. Для каждого запуска сохраняйте логи, метрики, checkpoint и команду запуска.

Оценка качества обучения ранжирующей модели

Снижение training loss не доказывает, что пользователи увидят лучший результат. Нужен отложенный набор запросов, одинаковый пул кандидатов и сравнение с исходной моделью.

Какие метрики использовать для reranker

  • MRR показывает, насколько высоко в среднем находится первый релевантный документ.
  • Recall@k измеряет, попал ли хотя бы один релевантный результат в первые k позиций.
  • Precision@k оценивает долю релевантных документов среди первых k.
  • NDCG@k учитывает порядок результатов и подходит для градуированной релевантности.
  • MAP полезна, когда для одного запроса есть несколько релевантных документов.

Выбирайте метрику по пользовательскому сценарию. Если человек видит первые три результата, смотрите показатели на малом k. Если система передает в RAG десять фрагментов, оценивайте именно этот диапазон.

Сравнение с универсальной моделью и baseline

Зафиксируйте тестовые запросы, пул кандидатов, длину входа, batch size и режим инференса. Сначала измерьте retriever без reranker, затем исходный cross-encoder и дообученную модель. Иначе сравнение смешает эффект поиска кандидатов с эффектом перестановки.

Пример с небольшой моделью, которая обучилась на 99 тысячах пар примерно за 30 минут и превзошла более крупные универсальные решения, полезен как ориентир возможного результата. Он не заменяет собственный benchmark: скорость зависит от GPU, длины текстов, реализации и размера батча, а качество зависит от распределения данных.

Для RAG дополнительно измеряйте задержку на один запрос и долю случаев, когда reranker поднимает правильный фрагмент в нужный top-k. Прирост offline-метрики не компенсирует задержку, которая нарушает требования продукта.

Ручной разбор ошибок после обучения

Соберите примеры, где новая модель ошиблась или изменила порядок сильнее всего. Разделите их по причинам:

  • совпадение терминов без совпадения намерения;
  • путаница версий, продуктов или ролей;
  • обрезанный фрагмент без необходимого контекста;
  • скрытый positive среди hard negatives;
  • переоценка длинного документа с большим числом совпадений.

Каждый класс ошибок связывайте с исправлением: новая разметка, другой способ нарезки, дополнительные hard negatives, изменение максимальной длины или фильтр по версии документа. Одна агрегированная метрика не показывает эти причины.

Что дает доменное дообучение и где его пределы

Почему небольшая специализированная модель может быть сильнее крупной универсальной

Специализированная модель получает более точный сигнал по целевой задаче. Она может лучше различать местные термины, внутренние формулировки и типовые связи между запросом и документом. Размер модели остается важным фактором, но в пределах одной задачи качество выборки способно перевесить число параметров.

Прирост зависит от домена, объема разметки, retriever и baseline. Узкое обучение может улучшить внутренний поиск и одновременно ухудшить работу с общими запросами. Проверяйте оба сценария, если модель будет обслуживать несколько тематик.

Когда дообучение ухудшает результат

  • Train и test содержат дубли или близкие формулировки.
  • В negative попали корректные документы.
  • Негативы слишком простые и не отражают ошибки retriever.
  • Доменный набор слишком узкий относительно будущих запросов.
  • Число эпох или learning rate привели к переобучению.
  • В production длина текста и структура входа отличаются от train.

Сигнал проблемы появляется, когда train loss продолжает снижаться, а validation и test перестают расти или ухудшаются. Еще один признак, модель выигрывает на доменных запросах и теряет качество на общем наборе. В этом случае увеличьте разнообразие данных, пересмотрите negative и попробуйте раннюю остановку.

Практический чек-лист перед публикацией модели

Минимальный воспроизводимый эксперимент

  1. Опишите роль reranker: сколько кандидатов он получает и какой top-k нужен пользователю.
  2. Соберите небольшой, но вручную проверенный набор пар.
  3. Зафиксируйте один baseline и один validation-набор.
  4. Выберите loss-функцию по типу разметки.
  5. Проведите один контролируемый запуск с фиксированными параметрами.
  6. Сравните метрики, ошибки и задержку с baseline.

Такой эксперимент позволяет понять, есть ли смысл расширять датасет и запускать новые итерации. При отсутствии прироста сначала проверяйте разметку и hard negatives, затем гиперпараметры.

Что сохранить вместе с обученной моделью

  • Конфигурацию базовой модели и токенизатора.
  • Версию и схему датасета.
  • Правила разметки positive, negative и спорных случаев.
  • Алгоритм майнинга hard negatives, retriever и параметры top-k.
  • Название loss-функции и ожидаемый формат входных полей.
  • Batch size, learning rate, число эпох, длину последовательности и precision.
  • Метрики на train, validation и test, включая baseline.
  • Известные ограничения и примеры ошибок.

Готовую модель достаточно использовать без fine-tune, если baseline закрывает требования по качеству и задержке. Доменное обучение оправдано при устойчивых ошибках, доступной проверенной разметке и понятной метрике успеха. Для RAG особенно полезно начинать с hard negatives из реального retriever, а затем проверять, улучшился ли порядок документов в том top-k, который видит языковая модель.

Общий принцип сохраняется для любого домена: сначала определите пользовательскую ошибку, затем создайте пример, который ее отражает, и только после этого меняйте модель или параметры обучения.

Подписаться на канал