Decay Ranker 總覽Compatible with Milvus 2.6.x

在傳統的向量搜尋中,搜尋結果的排序純粹取決於向量的相似性-向量在數學空間中的匹配程度。但在現實世界的應用中,內容是否真正相關往往不只取決於語意相似度。

請考慮這些日常情境:

  • 在新聞搜尋中,昨天的文章應該比三年前的類似文章排名更高

  • 餐廳搜尋器會優先搜尋 5 分鐘車程內的餐廳,而非 30 分鐘車程內的餐廳

  • 一個電子商務平台,能提升趨勢商品的排名,即使這些商品與搜尋查詢的相似度稍低。

這些情境都有一個共同的需求:平衡向量相似度與其他數值因素,例如時間、距離或知名度。

Milvus 的 Decay rankers 可根據數值字段值調整搜尋排名,從而滿足此需求。它們可讓您平衡向量相似性與資料的「新鮮度」、「接近度」或其他數值屬性,創造更直覺且與上下文相關的搜尋體驗。

使用注意事項

  • 衰減排名不能用於群組搜尋。

  • 用於衰減排名的欄位必須是數值 (INT8,INT16,INT32,INT64,FLOAT, 或DOUBLE)。

  • 每個衰減排名只能使用一個數值欄位。

  • 時間單位一致性:使用以時間為基礎的衰減排名時,origin,scale, 和offset 參數的單位必須與您的收集資料中使用的單位相符:

    • 如果您的資料集以為單位儲存時間戳記,請對所有參數使用秒。

    • 如果您的資料集以毫秒為單位儲存時間戳記,則所有參數都使用毫秒。

    • 如果您的集合以微秒為單位儲存時間戳記,則所有參數均使用微秒。

如何運作

衰減排序將時間或地理距離等數字因素納入排序過程中,增強了傳統向量搜尋的功能。整個過程遵循以下幾個階段

第一階段:計算標準化的相似性分數

首先,Milvus 會計算向量相似性分數並加以規範化,以確保比較結果一致:

  • 對於L2JACCARD距離指標 (較低的值表示較高的相似性):

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

    這將距離轉換成 0-1 之間的相似性分數,越高越好。

  • 對於IPCOSINEBM25公約 (分數越高表示匹配度越高):直接使用分數,無需標準化。

第二階段:計算衰減分數

接下來,Milvus 會根據數值字段值 (如時間戳記或距離),使用您選擇的衰減排名器計算衰減得分:

  • 每個衰減排名器將原始數值轉換為 0-1 之間的規範化相關性分數。

  • 衰減分數會根據項目與理想點的「距離」來表示其相關程度

具體的計算公式根據衰減排名器類型而有所不同。有關如何計算衰減分數的詳細資訊,請參閱高斯衰減指數衰減線性衰減的專用頁面。

第三階段:計算最終得分

最後,Milvus 結合規範化的相似度得分和衰減得分,產生最終的排名得分:

final_score = normalized_similarity_score × decay_score

在混合搜尋 (結合多向量領域) 的情況下,Milvus 取搜尋請求中最大的歸一化相似度得分:

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

例如,在混合搜尋中,如果一篇研究論文的向量相似度得分為 0.82,而以 BM25 為基礎的文字檢索得分為 0.91,Milvus 會先使用 0.91 作為基本相似度得分,然後再套用衰減因子。

衰減排序的實際應用

讓我們來看看衰減排序在實際情境中的應用-以時間為基礎的衰減來搜尋「AI 研究論文」

在這個範例中,衰減分數反映出相關性會隨著時間遞減-較新的論文會得到較接近 1.0 的分數,較舊的論文則會得到較低的分數。這些值是使用特定的衰減排名器計算出來的。如需詳細資訊,請參閱Choose the right decay ranker

論文

向量相似度

標準化相似度得分

發表日期

衰減分數

最終得分

最終排名

論文 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

如果不進行衰變重排,根據純向量相似度 (0.92) 計算,論文 B 的排名最高。但是,如果使用衰減重排:

  • 儘管相似度中等,論文 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