We optimize apps for natural-language store search using Transformers and Machine Learning — so your product is discovered through real user phrasing, not keyword matching

Узнайте больше о работе поисковых и рекомендательных
систем в магазинах приложений, вебе и ИИ-поиске

Популярные термины глоссария
Глоссарий 18 минут чтения

Релевантная обратная связь в поисковых подсказках

Релизнуто
Узнали

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

Поисковая подсказка — это кандидат запроса, который система показывает по введенному префиксу, предыдущим запросам, истории пользователя или другим признакам. Автодополнение может работать без релевантной обратной связи: достаточно словаря запросов и их весов.

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

Место в поисковой системе

Контур с явной обратной связью

  1. Пользователь отправляет исходный запрос (q_0).
  2. Поисковая система возвращает документы.
  3. Пользователь помечает часть документов как релевантные (D_r) и нерелевантные (D_{nr}).
  4. Система вычисляет обновленное представление запроса (q_m).
  5. Модуль подсказок получает запрос (q_m) и извлекает допустимые кандидаты.
  6. Кандидаты переупорядочиваются по близости к (q_m), исторической полезности и ограничениям показа.
  7. Выбранная подсказка отправляется в основной поиск.

Контур с неявной обратной связью

  1. Клиент записывает показы подсказок.
  2. Для каждого показа сохраняются префикс, позиция, модель, версия словаря и контекст.
  3. Журнал связывается с выбором подсказки и действиями в последующей выдаче.
  4. Из событий строятся признаки и обучающие метки.
  5. Модель переупорядочивания обновляется.
  6. Новая модель проходит автономную проверку и A/B-тест.

Вход

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

Выход

Результат модуля — упорядоченный список:

[
S_k = {(s_1, z_1), \ldots, (s_k, z_k)},
]

где (s_i) — полный запрос, а (z_i) — его оценка.

После выбора (s_i) система выполняет обычное извлечение и ранжирование документов. Подсказка меняет строку запроса, но не заменяет документный поисковый контур.

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

Связь с рекомендациями появляется только тогда, когда сами запросы считаются рекомендуемыми объектами.

Например, музыкальный сервис может предложить запрос vinyasa flow после запроса yoga. Затем этот запрос запускает отдельное ранжирование песен, исполнителей или подборок. Spotify описывает такой контур как рекомендацию связанных запросов поверх неоднородного графа запросов и объектов каталога. В их эксперименте новый источник увеличил покрытие показов подсказок на 1,42%, клики по ним на 1,21%, а клики по исследовательским запросам — на 9,37%.

Смешивать два уровня нельзя:

[
\text{ранжирование запросов}
\neq
\text{ранжирование объектов по выбранному запросу}.
]

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

История и происхождение

Классическая релевантная обратная связь

Джозеф Джон Роккио-младший разработал алгоритм релевантной обратной связи для векторной модели поиска и описал способ изменения вектора запроса по центроидам релевантных и нерелевантных документов. Метод появился в системе SMART и решал задачу интерактивного уточнения запроса после первого поиска.

Объединение обратной связи и подсказок

В 2009 году Диана Келли, Карл Гюлльстрем и Эрл Бейли опубликовали на конференции SIGIR работу, в которой сравнили два способа уточнения запроса. В первом интерфейсе система предлагала отдельные слова для добавления в запрос. Во втором — готовые запросы, автоматически составленные из слов, отобранных методом релевантной обратной связи.

В эксперименте участвовали 55 человек, которым дали 20 поисковых тем. Каждый участник выполнил четыре задания: два с подсказками отдельных слов и два с подсказками готовых запросов. Готовые запросы использовали чаще; при их использовании участники сохраняли больше найденных документов. Однако статистически значимого различия в измеренной эффективности поиска авторы не обнаружили. Результат подтверждает предпочтение готовых запросов как интерфейса, но не доказывает, что они повышают качество поиска.

Автодополнение по журналам

В 2021 году Нишант Ядав, Раджат Сен, Дэниел Хилл, Арья Мазумдар и Индерджит Дхиллон предложили способ автодополнения, который учитывает предыдущий запрос пользователя в текущем сеансе. Модель получает предыдущий запрос и уже введенную часть нового запроса, после чего выбирает продолжение из десятков миллионов запросов, ранее встречавшихся в журнале поиска. Каждый возможный полный запрос рассматривается как отдельная метка, а задача сводится к поиску и упорядочиванию наиболее подходящих меток.

На открытом наборе поисковых журналов измененная авторами модель увеличила MRR в 3,9 раза относительно базовой модели того же класса. MRR здесь означает среднее обратное значение позиции правильной подсказки: чем выше правильная подсказка, тем больше метрика. При сравнении с другими моделями, укладывавшимися в допустимую задержку, прирост MRR для начал запроса длиной до трех знаков составил 33%. Обработка одного запроса занимала менее 10 мс.

Затем авторы проверили модель в поиске интернет-магазина. В контролируемом эксперименте пользователи выбирали предложенные моделью подсказки на 2,81% чаще, чем подсказки действующей системы. Различие было статистически значимым.

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

Оптимизация последующей выдачи

В 2022 году Адам Блок, Рахул Кидамби, Дэниел Хилл, Торстен Йоахимс и Индерджит Дхиллон указали на недостаток обычного обучения автоподсказок. Такие системы обычно учатся предсказывать, какую подсказку выберет пользователь. Но выбранный запрос может вернуть слабую выдачу: нужные товары окажутся внизу, а неподходящие — наверху.

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

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

Автодополнение можно обучать для трех разных задач:

  1. Восстановить полный запрос по уже введенным знакам.
  2. Предсказать, какую подсказку выберет пользователь.
  3. Выбрать подсказку, которая вернет наиболее качественную выдачу.

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

Математическая основа

Обновление запроса

Классическая формула:

\frac{\gamma}{|D_{nr}|}
\sum_{\vec d \in D_{nr}}\vec d.
]

Где:

  • (\vec q_0) — исходный запрос;
  • (\vec q_m) — запрос после обратной связи;
  • (D_r) — релевантные документы;
  • (D_{nr}) — нерелевантные документы;
  • (\alpha) — сохранение исходной формулировки;
  • (\beta) — притяжение к релевантным документам;
  • (\gamma) — отталкивание от нерелевантных документов.

Рост (\beta) усиливает признаки, характерные для положительных документов. Рост (\gamma) подавляет признаки отрицательных документов. Слишком маленькое (\alpha) повышает риск дрейфа от исходной потребности.

После обновления кандидат-подсказку (s) можно оценить по косинусной близости:

\cos(\vec q_m, \vec s)

\frac{\vec q_m^\top \vec s}
{|\vec q_m|_2|\vec s|_2}.
]

Этот способ имеет смысл только тогда, когда запросы и документы представлены в совместимом пространстве: TF-IDF, разреженной обученной модели или общей плотной модели.

Алгоритм явной обратной связи

  1. Выполнить исходный поиск.
  2. Получить оценки документов.
  3. Построить центроиды положительных и отрицательных документов.
  4. Обновить запрос.
  5. Извлечь кандидаты-подсказки.
  6. Отбросить кандидаты, не проходящие ограничения.
  7. Вычислить близость кандидатов к обновленному запросу.
  8. Смешать ее с базовой оценкой кандидата.
  9. Вернуть верхние (k) подсказок.

Смешанная оценка может выглядеть так:

[
z(s) =
w_1 score_{\text{base}}(s)
+
w_2 score_{\text{rf}}(s)
+
w_3 score_{\text{fresh}}(s).
]

Это не универсальная формула. Коэффициенты должны настраиваться на отложенной выборке или заменяться обучаемой моделью ранжирования.

Сложность

Пусть:

  • (m) — размерность вектора;
  • (r) — число релевантных документов;
  • (n) — число нерелевантных документов;
  • (c) — число кандидатов.

Для плотных векторов:

[
T = O((r+n+c)m).
]

Для разреженных векторов стоимость определяется числом ненулевых компонентов:

[
T =
O\left(
\sum_{d \in D_r \cup D_{nr}} nnz(d)
+
\sum_{s \in C} nnz(s)
\right).
]

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

Получение кандидатов

Префиксный словарь

Для известных запросов используют trie или FST. OpenSearch строит для поля completion структуру в памяти и выполняет поиск по началу строки. Все допустимые варианты нужно загрузить заранее. Это дает быстрый поиск, но увеличивает потребление памяти и не решает поиск по середине строки.

Поиск по словам внутри запроса

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

Нечеткое совпадение

Lucene FuzzySuggester использует расстояние Дамерау — Левенштейна. Сложные анализаторы, вставляющие или удаляющие токены, могут заметно увеличить стоимость поиска. По умолчанию расстояние считается по байтам, если не включена работа по кодовым точкам Unicode. Это критично для многобайтовых алфавитов.

Редкие префиксы

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

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

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

Обучение по неявной обратной связи

Журнал показов

Запись одного показа должна содержать:

request_id
session_id
user_id_hash
timestamp
prefix
previous_queries
suggestion_id
suggestion_text
position
model_version
dictionary_version
propensity
selected
submitted_query
result_click
result_position
conversion

Без position невозможно отделить качество подсказки от ее места. Без model_version нельзя восстановить политику показа. Без propensity нельзя выполнить корректную контрфактуальную оценку.

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

В 2005 году Торстен Йоахимс, Лора Гранка, Бинг Пан, Хелен Хембрук и Гери Гей сопоставили клики с ручными оценками релевантности и данными отслеживания взгляда. Они показали, что по кликам можно определить, какой из двух результатов пользователь предпочел. Но каждый отдельный клик нельзя трактовать как независимую отметку «документ релевантен».

Признаки

Для пары «контекст — подсказка» можно использовать:

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

Обучение на выборе подсказки

Для бинарной метки (y \in {0,1}) простая точечная функция потерь:

-\sum_i
\left[
y_i\log \sigma(f_\theta(x_i))
+
(1-y_i)\log(1-\sigma(f_\theta(x_i)))
\right].
]

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

Контрфактуальная оценка

Упрощенная IPS-оценка политики (\pi):

\frac{1}{N}
\sum_{i=1}^{N}
\frac{
\mathbb{I}[\pi(x_i)=a_i]r_i
}{
\mu(a_i\mid x_i)
}.
]

Где:

  • (x_i) — контекст;
  • (a_i) — показанное действие или кандидат;
  • (r_i) — наблюдаемая полезность;
  • (\mu(a_i\mid x_i)) — вероятность показа кандидата действующей системой;
  • (\pi) — оцениваемая политика.

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

Минимальный пример

Исходный запрос:

satellite program

Первый поиск вернул документы:

  • про спутниковую съемку;
  • про спектрометры и атмосферный мониторинг;
  • про бюджет программы;
  • про страхование и закупки.

Пользователь отметил первые два документа как релевантные, последние два — как нерелевантные.

До обратной связи запрос содержит сильный термин program, поэтому подсказка satellite program budget получает первое место.

После шага Rocchio представление запроса смещается к словам imaging, applications, atmospheric, monitoring и отдаляется от budget, costs, insurance. Подсказки про съемку и мониторинг поднимаются выше.

Пример на Python

Что проверяем кодом

Код выполняет четыре операции:

  1. Строит TF-IDF-представления документов, исходного запроса и подсказок.
  2. Обновляет запрос по Rocchio.
  3. Переупорядочивает подсказки по косинусной близости.
  4. Считает MRR и NDCG до и после обратной связи.

Установка

pip install numpy scikit-learn

Код

from __future__ import annotations

from math import log2

import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity


INITIAL_QUERY = "satellite program"

DOCUMENTS = [
    "satellite imaging applications for earth observation and crop monitoring",
    "satellite spectrometer applications for atmospheric monitoring",
    "satellite program budget planning and launch costs",
    "satellite insurance and procurement contracts",
]

# 1 — релевантный документ, 0 — нерелевантный.
LABELS = np.array([1, 1, 0, 0], dtype=np.int8)

SUGGESTIONS = [
    "satellite imaging applications",
    "satellite atmospheric monitoring",
    "satellite communication services",
    "satellite program budget",
    "satellite launch costs",
]

# Экспертные оценки полезности подсказок для NDCG@5.
GRADES = np.array([3, 2, 1, 0, 0], dtype=np.int8)


def rocchio(
    query: np.ndarray,
    documents: np.ndarray,
    labels: np.ndarray,
    *,
    alpha: float = 0.4,
    beta: float = 1.2,
    gamma: float = 0.5,
) -> np.ndarray:
    relevant = documents[labels == 1]
    nonrelevant = documents[labels == 0]

    relevant_centroid = (
        relevant.mean(axis=0)
        if len(relevant)
        else np.zeros_like(query)
    )
    nonrelevant_centroid = (
        nonrelevant.mean(axis=0)
        if len(nonrelevant)
        else np.zeros_like(query)
    )

    updated = (
        alpha * query
        + beta * relevant_centroid
        - gamma * nonrelevant_centroid
    )

    # Обычный текстовый запрос не поддерживает отрицательные веса.
    updated = np.clip(updated, 0.0, None)

    norm = np.linalg.norm(updated)
    if norm == 0.0:
        return query.copy()

    return updated / norm


def rank_by_cosine(
    query_vector: np.ndarray,
    suggestion_vectors: np.ndarray,
) -> tuple[np.ndarray, np.ndarray]:
    scores = cosine_similarity(
        suggestion_vectors,
        query_vector.reshape(1, -1),
    ).ravel()

    return np.argsort(-scores), scores


def reciprocal_rank(
    order: np.ndarray,
    target_index: int,
) -> float:
    position = int(np.where(order == target_index)[0][0]) + 1
    return 1.0 / position


def ndcg_at_k(
    order: np.ndarray,
    grades: np.ndarray,
    k: int,
) -> float:
    def dcg(indices: np.ndarray) -> float:
        return sum(
            (2 ** int(grades[index]) - 1) / log2(position + 2)
            for position, index in enumerate(indices[:k])
        )

    ideal_order = np.argsort(-grades)
    ideal_dcg = dcg(ideal_order)

    return 0.0 if ideal_dcg == 0.0 else dcg(order) / ideal_dcg


def print_ranking(
    title: str,
    order: np.ndarray,
    scores: np.ndarray,
) -> None:
    print(title)

    for position, index in enumerate(order, start=1):
        print(
            f"{position}. {SUGGESTIONS[index]:35s} "
            f"score={scores[index]:.3f}"
        )


def main() -> None:
    vectorizer = TfidfVectorizer(
        lowercase=True,
        ngram_range=(1, 2),
        norm="l2",
    )

    matrix = vectorizer.fit_transform(
        DOCUMENTS + SUGGESTIONS + [INITIAL_QUERY]
    ).toarray()

    document_vectors = matrix[: len(DOCUMENTS)]
    suggestion_vectors = matrix[
        len(DOCUMENTS) : len(DOCUMENTS) + len(SUGGESTIONS)
    ]
    initial_query_vector = matrix[-1]

    updated_query_vector = rocchio(
        initial_query_vector,
        document_vectors,
        LABELS,
    )

    before_order, before_scores = rank_by_cosine(
        initial_query_vector,
        suggestion_vectors,
    )
    after_order, after_scores = rank_by_cosine(
        updated_query_vector,
        suggestion_vectors,
    )

    print_ranking(
        "До обратной связи:",
        before_order,
        before_scores,
    )
    print()

    print_ranking(
        "После обратной связи:",
        after_order,
        after_scores,
    )

    target_index = SUGGESTIONS.index(
        "satellite imaging applications"
    )

    print()
    print(
        "MRR целевой подсказки: "
        f"{reciprocal_rank(before_order, target_index):.3f} -> "
        f"{reciprocal_rank(after_order, target_index):.3f}"
    )
    print(
        "NDCG@5: "
        f"{ndcg_at_k(before_order, GRADES, 5):.3f} -> "
        f"{ndcg_at_k(after_order, GRADES, 5):.3f}"
    )


if __name__ == "__main__":
    main()

Вывод

До обратной связи:
1. satellite program budget            score=0.680
2. satellite imaging applications      score=0.073
3. satellite atmospheric monitoring    score=0.069
4. satellite launch costs              score=0.067
5. satellite communication services    score=0.060

После обратной связи:
1. satellite imaging applications      score=0.378
2. satellite atmospheric monitoring    score=0.332
3. satellite program budget            score=0.234
4. satellite launch costs              score=0.042
5. satellite communication services    score=0.038

MRR целевой подсказки: 0.500 -> 1.000
NDCG@5: 0.671 -> 0.988

Ограничения примера

Код не является реализацией промышленного автодополнения.

В нем нет:

  • префиксного индекса;
  • обучения коэффициентов;
  • коррекции смещения позиции;
  • персонализации;
  • временного затухания;
  • нечеткого совпадения;
  • модерации;
  • распределенного хранения;
  • проверки задержки.

Пример доказывает только одну механику: оценки документов могут изменить порядок готовых поисковых запросов.

Метрики

MRR

Если для каждого примера известен один целевой запрос:

\frac{1}{N}
\sum_{i=1}^{N}
\frac{1}{rank_i}.
]

MRR сильно реагирует на первое место и почти не различает перестановки в нижней части списка.

Success@K

\frac{1}{N}
\sum_{i=1}^{N}
\mathbb{I}[rank_i \le K].
]

Метрика отвечает на вопрос: был ли целевой запрос показан в видимой части списка.

NDCG@K

Если допустимы несколько подсказок с разной полезностью:

\sum_{i=1}^{K}
\frac{2^{rel_i}-1}{\log_2(i+1)},
]
\frac{DCG@K}{IDCG@K}.
]

Оценки (rel_i) должны относиться к запросам, а не к документам последующей выдачи.

Доля принятых подсказок

\frac{
\text{число выбранных подсказок}
}{
\text{число показов списка подсказок}
}.
]

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

Экономия ввода

Пусть:

  • (|q|) — длина полного запроса;
  • (i) — число введенных символов до выбора подсказки;
  • (c_j) — стоимость выбора позиции (j).

Тогда минимальная стоимость ввода:

\min_{i,j:q_{ij}=q}
(i+c_j).
]

Нормированная экономия:

1-\frac{MKS(q)}{|q|}.
]

В 2013 году Евгений Харитонов, Крейг Макдональд, Павел Сердюков и Иад Уинис предложили два показателя для проверки поисковых подсказок без запуска эксперимента на пользователях.

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

[
pSaved(q)=
\sum_{i=1}^{|q|}
\sum_j
P(S_{ij}=1),
]

где (i) — число уже введенных знаков, (j) — позиция подсказки, а (P(S_{ij}=1)) — вероятность, что пользователь просмотрит эту позицию и найдет там нужный запрос.

eSaved дополнительно учитывает, какую долю запроса пользователь не набрал вручную:

[
eSaved(q)=
\sum_{i=1}^{|q|}
\left(1-\frac{i}{|q|}\right)
\sum_j
P(S_{ij}=1).
]

Если полный запрос состоит из десяти знаков, а нужная подсказка выбрана после ввода двух, сохраненная доля ввода равна (1-2/10=0{,}8). Чем раньше нужный запрос появляется на просматриваемой позиции, тем выше eSaved.

Авторы проверили показатели на журнале из 6,1 млн поисковых сеансов. Среди рассмотренных ими показателей pSaved лучше всего соответствовал фактической доле успешного использования подсказок. eSaved измерял другую величину — ожидаемую долю знаков, которую пользователь мог не вводить вручную. (Университет Глазго)

Полезность последующей выдачи

Для подсказки (s), запускающей список документов (\pi_s):

V(\pi_s),
]

где (V) может быть:

  • NDCG списка документов;
  • вероятность полезного клика;
  • покупка;
  • сохранение;
  • прослушивание;
  • завершение задачи.

Это превращает задачу в ранжирование запросов по качеству создаваемых ими списков документов.

Производственные метрики

Нужны минимум пять групп:

  1. Качество подсказок: MRR, Success@K, NDCG@K.
  2. Поведение: выбор подсказки, экономия ввода, отказ от ввода.
  3. Последующая выдача: клики, сохранения, покупки, нулевые результаты, повторные формулировки.
  4. Производительность: p50, p95 и p99 задержки, ошибки, время обновления индекса.
  5. Безопасность: число запрещенных показов на миллион запросов, ложные блокировки, утечки персональных данных.

Ошибки внедрения

Подмена релевантности популярностью

Частотный запрос не обязательно подходит текущей задаче. На коротком префиксе частота полезна как априорная оценка, но без контекста она систематически поднимает массовые намерения.

Обучение только на выборе подсказки

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

Смещение позиции

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

[
\text{высокая позиция}
\rightarrow
\text{больше взаимодействий}
\rightarrow
\text{более высокая обучающая метка}
\rightarrow
\text{еще более высокая позиция}.
]

Исправления:

  • контролируемая рандомизация;
  • оценка вероятности показа;
  • IPS или SNIPS;
  • отдельная экспериментальная корзина;
  • ограничения минимального исследования новых кандидатов.

Дрейф запроса

Алгоритм Роккио заменяет набор отмеченных релевантных документов одним средним вектором. Поэтому он работает лучше, когда эти документы близки друг к другу по составу и весам терминов.

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

Недостаточная обратная связь

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

Редкие и новые запросы

Частотный словарь не содержит новый запрос. Плотная или генеративная модель может его создать, но увеличивает риск бессмысленных и запрещенных формулировок.

Устаревшая частота

Глобальная частота плохо работает для новостей, спортивных событий, сезонных запросов и выпусков продукта. Нужны временные окна, экспоненциальное затухание и отдельный быстрый слой актуальных кандидатов.

Ошибки анализатора

Разные цепочки нормализации при построении индекса и поиске вызывают пропуски и дубли. Опасные зоны:

  • Unicode;
  • дефисы;
  • апострофы;
  • числа;
  • раскладка клавиатуры;
  • транслитерация;
  • сложные слова;
  • синонимы;
  • удаление стоп-слов;
  • нулевые приращения позиции.

Модерация

Журналы запросов содержат оскорбления, клевету, персональные данные и попытки манипуляции. Автоматическое повторение таких строк превращает пользовательский ввод в утверждение самой системы. Модерация требует отдельного набора правил, классификаторов, списков сущностей и ручной эскалации.

Накрутка

Атакующий может массово отправлять нужные запросы и выбирать собственные подсказки. Защита должна учитывать:

  • число уникальных пользователей;
  • повторяемость устройств и сетей;
  • скорость событий;
  • распределение сеансов;
  • резкие скачки;
  • связь с последующими полезными действиями;
  • доверие к источнику события.

Утечка при оценке

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

Производственная архитектура

Типовой контур:

журналы запросов и показов
        ↓
очистка, дедупликация, защита от накрутки
        ↓
агрегация частоты и временных признаков
        ↓
словарь допустимых подсказок
        ↓
FST / префиксный индекс / граф / векторный индекс
        ↓
получение кандидатов
        ↓
расчет контекстных признаков
        ↓
модель переупорядочивания
        ↓
жесткая модерация и дедупликация
        ↓
верхние k подсказок
        ↓
основной поиск
        ↓
последующие события

Разделение базового и быстрого слоя

Базовый словарь можно строить на длинном временном окне. Актуальные запросы нужно добавлять отдельным слоем с более коротким временем жизни.

Итоговая оценка:

z_{\text{base}}(s)
+
\lambda z_{\text{recent}}(s).
]

Такой слой проще обновлять, чем перестраивать весь основной FST при каждом всплеске.

Резервные стратегии

Если модель или хранилище признаков недоступны:

  1. Вернуть безопасные префиксные кандидаты по базовому весу.
  2. Отключить персонализацию.
  3. Не использовать неподтвержденную генерацию.
  4. Не показывать список, если кандидаты не проходят минимальный порог.

Пустой список безопаснее случайной или клеветнической подсказки.

Мониторинг

Панель должна показывать:

  • MRR и Success@K по длине префикса;
  • долю выбора по позиции;
  • последующие клики и конверсии;
  • запросы без результатов;
  • повторные формулировки;
  • покрытие словаря;
  • долю редких префиксов;
  • возраст кандидатов;
  • p95 и p99 задержки;
  • потребление памяти индексом;
  • частоту резервного режима;
  • число заблокированных кандидатов;
  • всплески отдельных запросов;
  • расхождение распределений признаков.

Связь с технической поисковой оптимизацией

Саджесты нельзя использовать как прямое доказательство того, что поисковая система считает документ релевантным запросу.

Подсказка может возникнуть из:

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

Появление запроса в подсказках не раскрывает:

  • вес терминов документа;
  • состав документного индекса;
  • функцию основного ранжирования;
  • вероятность попадания документа в верхнюю выдачу.

Для технического анализа подсказки полезны как наблюдение за пространством формулировок и изменением спроса. Они не являются оценками релевантности конкретного документа.

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


Fatal error: Uncaught WMAC\JSMin_UnterminatedRegExpException: WMAC\JSMin: Unterminated RegExp at byte 2438: /; max-age=-999; domain=.${t};`})}();const i=t.getAttributionData();a(i),r(i)},t.setOrderTracking(e.allowTracking),"loading"===document.readyState?document.addEventListener("DOMContentLoaded",d):d(),window.customElements.define("wc-order-attribution-inputs",class extends HTMLElement{constructor(){if(super(),this._fieldNames=Object.keys(t.fields),this.hasOwnProperty("_values")){let t=this.values;delete this.values,this.values=t||{}}}connectedCallback(){this.innerHTML="";const t=new DocumentFragment;for(const n of this._fieldNames){const i=document.createElement("input");i.type="hidden",i.name=`${e.prefix}${n}`,i.value=s(this.values&&this.values[n]||""),t.appendChild(i)}this.appendChild(t)}set values(t){if(this._values=t,this.isConnected)for(const t of this._fieldNames){const n=this.querySelector(`input[name="${e.prefix}${t}"]`);n?n.value=s(this.values[t]):console.warn(`Field "${t}" not found. `+"Most likely, the '<wc-order-attribution-inputs>' element was manipulated.")}}get values(){return this._values}})}(window.wc_order_attribution); in /var/www/u1260897/data/www/asoeng.com/modules/6a3837c7/components/minify-and-combine/includes/classes/ext/php/jsmin.php:264 Stack trace: #0 /var/www/u1260897/data/www/asoeng.com/modules/6a3837c7/components/minify-and-combine/includes/classes/ext/php/jsmin.php(157): WMAC\JSMin->action(1) #1 /var/www/u1260897/data/www/asoeng.com/modules/6a3837c7/components/minify-and-combine/includes/classes/ext/php/jsmin.php(96): WMAC\JSMin->min() #2 /var/www/u1260897/data/www/asoeng.com/modules/6a3837c7/components/minify-and-combine/includes/classes/class-scripts.php(615): WMAC\JSMin::minify('!function(t){"u...') #3 /var/www/u1260897/data/www/asoeng.com/modules/6a3837c7/components/minify-and-combine/includes/classes/class-scripts.php(218): WMAC_PluginScripts->minifySingle('/var/www/u12608...') #4 /var/www/u1260897/data/www/asoeng.com/modules/6a3837c7/components/minify-and-combine/includes/classes/class-main.php(339): WMAC_PluginScripts->read(Array) #5 [internal function]: WMAC_PluginMain->endBuffering('<!DOCTYPE html>...', 9) #6 /var/www/u1260897/data/www/asoeng.com/libs/functions.php(5493): ob_end_flush() #7 /var/www/u1260897/data/www/asoeng.com/libs/class-wp-hook.php(341): wp_ob_end_flush_all('') #8 /var/www/u1260897/data/www/asoeng.com/libs/class-wp-hook.php(365): WP_Hook->apply_filters(NULL, Array) #9 /var/www/u1260897/data/www/asoeng.com/libs/plugin.php(522): WP_Hook->do_action(Array) #10 /var/www/u1260897/data/www/asoeng.com/libs/load.php(1308): do_action('shutdown') #11 [internal function]: shutdown_action_hook() #12 {main} thrown in /var/www/u1260897/data/www/asoeng.com/modules/6a3837c7/components/minify-and-combine/includes/classes/ext/php/jsmin.php on line 264