Обзор Decay RankerCompatible with Milvus 2.6.x

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

Рассмотрим эти повседневные сценарии:

  • Поиск новостей, в котором вчерашняя статья должна занимать более высокое место, чем аналогичная статья трехлетней давности.

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

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

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

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

Примечания по использованию

  • Ранжирование по распаду нельзя использовать при поиске по группам.

  • Поле, используемое для ранжирования по распаду, должно быть числовым (INT8, INT16, INT32, INT64, FLOAT или DOUBLE).

  • Каждый ранжировщик распада может использовать только одно числовое поле.

  • Согласованность единиц времени: При использовании ранжирования по времени единицы измерения для параметров origin, scale и offset должны соответствовать единицам измерения, используемым в данных вашей коллекции:

    • Если в коллекции хранятся временные метки в секундах, используйте секунды для всех параметров.

    • Если в коллекции хранятся временные метки в миллисекундах, используйте миллисекунды для всех параметров

    • Если ваша коллекция хранит временные метки в микросекундах, используйте микросекунды для всех параметров.

Как это работает

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

Этап 1: Вычисление нормализованных баллов сходства

Сначала Milvus рассчитывает и нормализует баллы сходства векторов, чтобы обеспечить последовательное сравнение:

  • Для метрик расстояния L2 и JACCARD (где меньшие значения указывают на большую схожесть):

    normalized_score = 1.0 - (2 × arctan(score))/π
    

    Расстояния преобразуются в баллы сходства от 0 до 1, где большее значение лучше.

  • Для метрик IP, COSINE и BM25 (где более высокие значения уже указывают на лучшее сходство): Баллы используются напрямую без нормализации.

Этап 2: Расчет оценок распада

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

  • Каждый ранжировщик распада преобразует необработанные числовые значения в нормализованные оценки релевантности в диапазоне от 0 до 1.

  • Балл распада отражает степень релевантности элемента в зависимости от его "расстояния" до идеальной точки.

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

Этап 3: Вычисление итоговых оценок

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

final_score = normalized_similarity_score × decay_score

В случае гибридного поиска (сочетающего несколько векторных полей) Milvus берет максимальный нормализованный балл сходства среди поисковых запросов:

final_score = max([normalized_score₁, normalized_score₂, ..., normalized_scoreₙ]) × decay_score

Например, если научная статья получила 0,82 балла за векторное сходство и 0,91 балла за поиск текста на основе BM25 в гибридном поиске, Milvus использует 0,91 балла в качестве базового балла сходства перед применением коэффициента распада.

Ранжирование по распаду в действии

Давайте посмотрим на ранжирование по распаду в практическом сценарии - поиск "исследовательских работ по искусственному интеллекту" с распадом по времени:

В этом примере показатели распада отражают, как релевантность уменьшается со временем - более новые статьи получают оценки ближе к 1,0, более старые - ниже. Эти значения рассчитываются с помощью специального ранжировщика распада. Подробнее см. в разделе Выбор правильного ранжировщика распада.

Статья

Векторное сходство

Нормализованный балл сходства

Дата публикации

Оценка распада

Итоговый балл

Итоговый ранг

Бумага A

Высокий

0,85 (COSINE)

2 недели назад

0.80

0.68

2

Бумага B

Очень высокий

0,92 (COSINE)

6 месяцев назад

0.45

0.41

3

Бумага C

Средний

0.75 (COSINE)

1 день назад

0.98

0.74

1

Бумага D

Средне-высокий

0.76 (COSINE)

3 недели назад

0.70

0.53

4

Без ранжирования по распаду бумага B заняла бы самое высокое место на основе чистого векторного сходства (0,92). Однако с применением ранжирования по распаду:

  • Работа C поднимается на позицию №1, несмотря на среднее сходство, потому что она очень свежая (опубликована вчера).

  • Работа B опускается на позицию № 3, несмотря на отличное сходство, потому что она относительно старая.

  • Работа D использует расстояние L2 (где меньше - лучше), поэтому ее оценка нормализуется с 1,2 до 0,76 перед применением распада

Выберите правильный ранжировщик распада

Milvus предлагает различные ранжировщики распада - gauss, exp, linear, каждый из которых предназначен для конкретных случаев использования:

Ранжировщик распада

Характеристики

Идеальные сценарии использования

Пример сценария

Гаусс (gauss)

Естественный постепенный спад, который умеренно расширяется

  • Общий поиск, требующий сбалансированных результатов

  • Приложения, в которых пользователи интуитивно чувствуют расстояние

  • Когда умеренное расстояние не должно сильно ухудшать результаты

При поиске ресторана качественные заведения, расположенные на расстоянии 3 км, остаются доступными для обнаружения, хотя и ранжируются ниже, чем близлежащие варианты

Экспоненциальный (exp)

Сначала быстро снижается, но потом сохраняет длинный хвост

  • Новостные ленты, где критична повторяемость

  • Социальные сети, где должен преобладать свежий контент

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

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

Линейный (linear)

Последовательный, предсказуемый спад с четкой границей

  • Приложения с естественными границами

  • Услуги с ограничениями по расстоянию

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

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

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

Пример реализации

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

Перед использованием функций распада необходимо создать коллекцию с соответствующими числовыми полями (например, временными метками, расстояниями и т. д.), которые будут использоваться для расчетов распада. Полные рабочие примеры, включающие настройку коллекции, определение схемы и вставку данных, см. в разделе Учебник: Реализация ранжирования по времени в Milvus.

Создание ранжировщика распада

Чтобы реализовать ранжирование по распаду, сначала определите объект Function с соответствующей конфигурацией:

from pymilvus import Function, FunctionType

# Create a decay function for timestamp-based decay
# Note: All time parameters must use the same unit as your collection data
decay_ranker = Function(
    name="time_decay",                  # Function identifier
    input_field_names=["timestamp"],    # Numeric field to use for decay
    function_type=FunctionType.RERANK,  # Must be set to RERANK for decay rankers
    params={
        "reranker": "decay",            # Specify decay reranker. Must be "decay"
        "function": "gauss",            # Choose decay function type: "gauss", "exp", or "linear"
        "origin": int(datetime.datetime(2025, 1, 15).timestamp()),    # Reference point (seconds)
        "scale": 7 * 24 * 60 * 60,      # 7 days in seconds (must match collection data unit)
        "offset": 24 * 60 * 60,         # 1 day no-decay zone (must match collection data unit)
        "decay": 0.5                    # Half score at scale distance
    }
)
import io.milvus.v2.service.vector.request.ranker.DecayRanker;

import java.time.ZoneId;
import java.time.ZonedDateTime;

ZonedDateTime zdt = ZonedDateTime.of(2025, 1, 25, 0, 0, 0, 0, ZoneId.systemDefault());

DecayRanker ranker = DecayRanker.builder()
        .name("time_decay")
        .inputFieldNames(Collections.singletonList("timestamp"))
        .function("gauss")
        .origin(zdt.toInstant().toEpochMilli())
        .scale(7 * 24 * 60 * 60)
        .offset(24 * 60 * 60)
        .decay(0.5)
        .build();


import {FunctionType } from "@zilliz/milvus2-sdk-node";

const decayRanker = {
  name: "time_decay",
  input_field_names: ["timestamp"],
  function_type: FunctionType.RERANK,
  params: {
    reranker: "decay",
    function: "gauss",
    origin: new Date(2025, 1, 15).getTime(),
    scale: 7 * 24 * 60 * 60,
    offset: 24 * 60 * 60,
    decay: 0.5,
  },
};

// go
# restful

Параметр

Требуемый?

Описание

Значение/пример

name

Да

Идентификатор вашей функции, используемый при выполнении поиска. Выберите описательное имя, соответствующее вашему сценарию использования.

"time_decay"

input_field_names

Да

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

Это должно быть поле в вашей коллекции, содержащее соответствующие числовые значения. Поддерживаются INT8/16/32/64, FLOAT, DOUBLE.

["timestamp"]

function_type

Да

Указывает тип создаваемой функции.

Должно быть установлено значение RERANK для всех ранжировщиков распада.

FunctionType.RERANK

params.reranker

Да

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

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

"decay"

params.function

Да

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

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

"gauss", "exp", или "linear"

params.origin

Да

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

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

  • Для временных меток: текущее время (например, int(time.time())).

  • Для геолокации: текущие координаты пользователя.

params.scale

Да

Расстояние или время, за которое релевантность снижается до значения decay. Определяет, как быстро снижается релевантность.

Для снижения релевантности на основе времени единица измерения времени должна соответствовать данным вашей коллекции.

Большие значения создают более постепенное снижение релевантности; меньшие значения создают более резкое снижение.

  • Для времени: период в секундах (например, 7 * 24 * 60 * 60 в течение 7 дней).

  • Для расстояния: метры (например, 5000 для 5 км).

params.offset

Нет

Создает "зону без распада" вокруг origin, где предметы сохраняют полную оценку (оценка распада = 1,0).

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

Предметы в этом диапазоне origin сохраняют максимальную релевантность.

  • Для времени: период в секундах (например, 24 * 60 * 60 за 1 день).

  • Для расстояния: метры (например, 500 для 500 м).

params.decay

Нет

Значение балла на расстоянии scale, контролирует крутизну кривой. Более низкие значения создают более крутые кривые спада; более высокие значения создают более плавные кривые спада.

Должно быть от 0 до 1.

0.5 (по умолчанию)

Определив свой ранжировщик распада, вы можете применить его в процессе поиска, передав параметр ranker:

# Use the decay function in standard vector search
results = milvus_client.search(
    collection_name,
    data=[your_query_vector], # Replace with your query vector
    anns_field="vector_field",
    limit=10,
    output_fields=["document", "timestamp"],  # Include the decay field in outputs to see values
    ranker=decay_ranker,                      # Apply the decay ranker here
    consistency_level="Strong"
)
import io.milvus.v2.service.vector.request.SearchReq;
import io.milvus.v2.service.vector.response.SearchResp;
import io.milvus.v2.service.vector.request.data.EmbeddedText;

SearchReq searchReq = SearchReq.builder()
        .collectionName(COLLECTION_NAME)
        .data(Collections.singletonList(new EmbeddedText("search query")))
        .annsField("vector_field")
        .limit(10)
        .outputFields(Arrays.asList("document", "timestamp"))
        .functionScore(FunctionScore.builder()
                .addFunction(ranker)
                .build())
        .build();
SearchResp searchResp = client.search(searchReq);
const result = await milvusClient.search({
  collection_name: "collection_name",
  data: [your_query_vector], // Replace with your query vector
  anns_field: "dense",
  limit: 10,
  output_fields: ["document", "timestamp"],
  rerank: ranker,
  consistency_level: "Strong",
});
// go
# restful