AACsearch
Relevance Studio

Обучение ранжированию (LTR)

Конвейер LTR в Relevance Studio — обратная связь по кликам, коррекция позиционного смещения, обучение, версионирование моделей, A/B-тесты и активация. Как интерпретировать значимость z-критерия и выбрать победителя.

Learning to Rank (LTR) — замкнутый цикл обратной связи, превращающий реальные клики пользователей в выученную модель ранжирования. Studio поставляет четыре панели, соответствующие четырём этапам: Обратная связь по кликам, Прогоны обучения, Модели и A/B-тесты.

Конвейер кратко

клики (SearchUsageEvent)

   ▼   коррекция позиционного смещения (debias)

   ▼   обучение (сегодня — shim-алгоритм; нативные LightGBM/XGBoost отложены)

   ▼   артефакт модели + метрики (NDCG, MRR, AUC)

   ▼   A/B-тест: сплит трафика, значимость по z-критерию

   ▼   активация победителя → ранжирование на пути чтения

Каждый этап обратим. До деактивации плохой модели — один клик: прежняя активная модель всегда сохраняется.

1 · Обратная связь по кликам

Панель Обратная связь по кликам показывает сырой сигнал, который идёт обучателю. Три числа на индекс:

  • клики-с-контекстом — сколько событий click пришло с валидными queryId и position. Без queryId нельзя связать клик с поиском.
  • коррекция позиционного смещения — действующая кривая дебиаса. Позиционное смещение — известный эффект, когда первая позиция кликается независимо от релевантности. Скорректированный скор примерно равен observed_ctr / propensity[position], где propensity подбирается по индексу.
  • используемые строки для обучения — после дебиаса и фильтра zero-result сколько строк пригодно для обучения.

Если пригодных строк меньше ~5 тыс., Training отказывается стартовать и показывает баннер «недостаточно сигнала». Ниже этого порога обученная модель переобучается и проигрывает статическому ранкеру.

2 · Прогоны обучения

Прогон обучения создаёт артефакт модели из выбранного окна обратной связи. В Studio вы выбираете:

  • Окно — последние 7, 30 или 90 дней.
  • Алгоритм — сейчас shim (единственная опция в релизных сборках). Нативные LightGBM и XGBoost — отложены, см. ниже.
  • Признаки — преднастроенный набор (текстовая релевантность, свежесть, цена, популярность, кохорта категории). Кастомные признаки — в roadmap.

Прогон показывает прогресс и при завершении выводит три метрики:

МетрикаСмысл
NDCG@10Normalized discounted cumulative gain в топ-10 — основная метрика
MRRMean reciprocal rank первого кликнутого результата
AUCПлощадь под кривой click vs non-click

Текущий статус shim (май 2026)

Отгружаемый LTR-обучатель — shim-алгоритм: упрощённый линейный комбинатор, подбирающий небольшое число весов поверх предвычисленных признаков. Он намеренно консервативен и работает за фичефлагом ltr.shim.

Полные нативные LightGBM- и XGBoost-обучатели отложены. Они реализованы за флагом ltr.native, но не в GA — нужны дополнительные нагрузочные тесты и rollout реестра моделей. Сейчас shim — единственный поддерживаемый алгоритм; ожидайте прирост NDCG@10 в 5–15% относительно неран­жированного baseline, а не 25–40%, как в некоторых статьях. Клиенты, которым уже сегодня нужен нативный LightGBM, могут обратиться в поддержку для приватного preview.

3 · Модели

Каждый успешный прогон создаёт модель в панели Модели. Модели — неизменяемые артефакты, несут:

  • версионированный ID (mdl_<random>), время создания, исходный прогон,
  • кортеж метрик (NDCG@10, MRR, AUC),
  • набор признаков и окно, на котором обучалась,
  • статус деплоя: draft, in_ab_test, active или archived.

Модель в active — это то, что путь чтения консультирует на каждый поисковый запрос по индексу. В каждый момент на индекс ровно одна активная модель.

4 · A/B-тесты

Модель не переводится из draft сразу в active. Вместо этого запускается A/B-тест, разделяющий живой трафик между моделью-кандидатом (плечо B) и текущей активной моделью (плечо A, контроль).

Запуск A/B-теста

В Studio → LTR → A/B-тесты → Новый тест:

// То, что Studio делает под капотом — также вызываемо напрямую
import { client } from "@repo/api/client";

const test = await client.ltr.abTests.create.call({
  indexSlug: "products",
  controlModelId: "mdl_active_now",
  variantModelId: "mdl_candidate",
  trafficSplit: 0.10, // 10% живого трафика видят плечо B
  primaryMetric: "ctr", // ctr | cvr | ndcg10
  minSampleSize: 50_000, // запросов на плечо до подсчёта значимости
});

Сплит трафика идёт на запрос, а не на пользователя — поэтому один и тот же пользователь между сессиями может попасть в разные плечи. На плече B применяется кандидат; плечо A продолжает использовать текущую активную модель.

Чтение результатов

Пока тест идёт, панель показывает живые счётчики по плечам:

  • n — запросов, попавших в плечо.
  • clicks, ctr, cvr — основные метрики, обновляются раз в 5 минут.
  • z-оценка — стандартизованная разница плеч B и A по основной метрике.
  • p-value — двусторонний.
  • решениеrunning, significant_win, significant_loss, ±borderline или no_effect.

Как интерпретировать значимость

Обучатель использует стандартный z-критерий двух пропорций, считая каждый запрос независимым испытанием Бернулли по основной метрике. Пороги:

РешениеДиапазон zЧто значит
significant_winz ≥ +1.96 (p ≤ 0.05)Кандидат превзошёл контроль с уверенностью 95%.
significant_lossz ≤ −1.96Кандидат проиграл с уверенностью 95% — остановите тест.
±borderline1.65 ≤ |z| < 1.96Уверенность 90–95% — ждать данных или принимать решение заранее.
no_effect|z| < 1.65 и n ≥ minSampleSizeРазличие не различимо при выбранной выборке.
runningn < minSampleSizeДанных пока недостаточно.

Полоса ±borderline намеренно вынесена в отдельное решение, потому что у большинства продуктовых команд есть предварительное правило вроде «релизим при 90%, если дельта положительная; держим при 95%, если отрицательная». Studio в borderline сама ничего не промотирует — нужно, чтобы человек нажал Активировать победителя.

Подсказка по размеру выборки

Default minSampleSize — 50 000 запросов на плечо. При сплите 10% индекс с 50 тыс. запросов/день достигает значимости примерно за 11 дней для эффекта в 2 п.п. CTR. Меньшим индексам нужно 3–4 недели. Панель Studio показывает живой ETA.

Активация победителя

Когда решение — significant_win (или вы решили релизить на ±borderline), нажмите Активировать победителя в A/B-панели. Это:

  1. Переводит модель-вариант в статус active.
  2. Понижает прежнюю активную модель до archived (её можно вернуть одним кликом, если новая модель регрессирует в проде).
  3. Закрывает A/B-тест итоговым отчётом.
  4. За ~60 секунд (TTL LRU policy-cache) переключает 100% живого трафика на новую модель.
await client.ltr.abTests.activateWinner.call({
  testId: test.id,
  // опциональная проверка безопасности — откажет, если живое решение
  // между fetch и call ушло от `significant_win`
  expectedDecision: "significant_win",
});

Если после активации нужен откат — панель Модели хранит прежнюю активную модель как archived и даёт Реактивировать одним кликом.

Лучшие практики

  • Всегда запускайте A/B-тест. Даже модель, побеждающая по NDCG в offline, может регрессировать по живому CTR — позиционное смещение и эффекты подачи не видны в offline-метриках.
  • Выбирайте метрику, отражающую бизнес. CTR — default; для конверсионных каталогов ставьте primaryMetric: "cvr".
  • Не «peek-and-stop». z-критерий предполагает фиксированный объём выборки — подглядывание до minSampleSize и ранний стоп раздувают ложноположительные срабатывания. Зафиксируйте выборку, отойдите, принимайте решение потом.
  • Архивируйте агрессивно. Держите не более 5–10 архивных моделей на индекс. Реестр недорог, но журнал аудита читается легче при меньшем числе строк.

Связанные материалы

On this page