ディケイ・ランカーの概要Compatible with Milvus 2.6.x
従来のベクトル検索では、結果は純粋にベクトルの類似性によってランク付けされる。しかし、実世界のアプリケーションでは、コンテンツを本当に関連性のあるものにするかどうかは、意味的な類似性以上に左右されることが多い。
日常的なシナリオを考えてみよう:
昨日の記事が3年前の類似記事よりも上位に表示されるべきニュース検索
車で30分かかる店よりも、5分以内の店を優先するレストラン検索。
検索クエリとの類似性が多少低くても、トレンド商品を上位に表示するEコマース・プラットフォーム
これらのシナリオはすべて、ベクトルの類似性と、時間、距離、人気などの他の数値要素とのバランスをとるという共通のニーズを共有している。
Milvusのディケイランカーは、数値フィールドの値に基づいて検索順位を調整することで、このニーズに対応します。これにより、ベクトルの類似性とデータの「新鮮さ」、「近さ」、またはその他の数値的特性とのバランスをとることができ、より直感的で文脈に関連した検索体験を生み出すことができます。
使用上の注意
ディケイランキングはグループ化検索では使用できません。
ディケイランキングに使用するフィールドは数値(
INT8,INT16,INT32,INT64,FLOAT, またはDOUBLE)でなければなりません。各ディケイランカーは1つの数値フィールドしか使用できません。
時間単位の一貫性:時間ベースのディケイランキングを使用する場合、
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、BM25ベースのテキスト検索で0.91のハイブリッド検索を行った場合、Milvusは減衰係数を適用する前に0.91を基本類似度スコアとして使用します。
ディケイ・ランキングの実例
実用的なシナリオでディケイ・ランキングを見てみよう-時間ベースのディケイを使った「AI研究論文」の検索である:
この例では、減衰スコアは時間とともに関連性がどのように低下するかを反映しています。これらの値は、特定の減衰ランカーを使用して計算されます。詳細は「正しいディケイランカーを選ぶ」を参照してください。
論文 |
ベクトル類似度 |
正規化類似度スコア |
出版日 |
減衰スコア |
最終スコア |
最終順位 |
|---|---|---|---|---|---|---|
論文A |
高い |
0.85 ( |
2週間前 |
0.80 |
0.68 |
2 |
ペーパーB |
非常に高い |
0.92 ( |
6ヶ月前 |
0.45 |
0.41 |
3 |
ペーパーC |
ミディアム |
0.75 ( |
1日前 |
0.98 |
0.74 |
1 |
ペーパーD |
中-高 |
0.76 ( |
3週前 |
0.70 |
0.53 |
4 |
減衰リランキングがなければ、論文Bは純粋なベクトル類似度(0.92)に基づいて最上位にランクされる。しかし、ディケイ・リランキングを適用すると
論文Cは、類似度が中程度であるにもかかわらず1位に躍り出た。
論文Bは、比較的古いため、優れた類似性にもかかわらず3位にランクダウン。
論文DはL2距離(低いほど良い)を使用しているため、減衰を適用する前にスコアが1.2から0.76に正規化される。
適切なディケイランカーを選択する
Milvusは、gauss 、exp 、linear 、それぞれ特定のユースケース向けに設計された、異なるディケイランカーを提供しています:
ディケイランカー |
特徴 |
理想的な使用例 |
シナリオ例 |
|---|---|---|---|
ガウシアン ( |
適度に広がる自然な感じの緩やかな減少 |
|
レストラン検索において、3km離れた場所にある質の高いレストランは、近隣のレストランよりも低いランクではあるが、発見可能なままである。 |
指数 ( |
最初は急激に減少するが、ロングテールを維持する |
|
ニュースアプリでは、昨日の記事は1週間前のコンテンツよりもはるかに上位にランクされるが、関連性の高い古い記事が表示されることもある。 |
リニア ( |
一貫性があり、予測可能な減少で、明確なカットオフがある。 |
|
イベント・ファインダーでは、2週間先のウィンドウを超えるイベントはまったく表示されません。 |
各ディケイ・ランカーのスコアの計算方法や具体的な減少パターンについての詳細は、専用のドキュメントを参照してください:
実装例
ディケイランカーは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
パラメータ |
必須か? |
説明 |
値/例 |
|---|---|---|---|
|
はい |
検索実行時に使用する関数の識別子。ユースケースに関連する説明的な名前を選択してください。 |
|
|
はい |
減衰スコア計算用の数値フィールド。どのデータ属性が減衰の計算に使用されるかを決定する(例えば、時間ベースの減衰にはタイムスタンプ、位置ベースの減衰には座標)。 関連する数値を含むコレクション内のフィールドである必要があります。INT8/16/32/64、FLOAT、DOUBLEをサポート。 |
|
|
はい |
作成する関数のタイプを指定する。 すべての減衰ランカーに対して |
|
|
はい |
使用するリランキング方法を指定します。 ディケイランキング機能を有効にするには、 |
|
|
はい |
どの数学的ディケイランカーを適用するかを指定します。関連性低下の曲線形状を決定する。 適切な関数を選択するためのガイダ ンスについては、「適切なディケイランカーを選 択する」のセクションを参照してください。 |
|
|
はい |
減衰スコアを計算する基準点。この値のアイテムは最大関連性スコアを獲得する。 時間ベースの減衰の場合、時間単位は収集データと一致する必要があります。 |
|
|
はい |
関連性が 時間ベースの減衰の場合、時間単位は収集データと一致する必要があります。 値が大きいほど関連性が緩やかに低下し、値が小さいほど 急激に低下する。 |
|
|
いいえ |
時間ベースの減衰の場合、時間単位は収集データと一致する必要があります。
|
|
|
いいえ |
0 から 1 の間でなければならない。 |
|
標準ベクトル検索に適用
減衰ランカーを定義した後、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