Decay Rankerの概要
従来のベクトル検索では、結果は純粋にベクトル類似度(数学的空間でベクトルがどれだけ密接に一致するか)によってランク付けされます。しかし、現実世界のアプリケーションでは、コンテンツを真に関連性の高いものにする要素は、意味的類似度だけではありません。
次のような日常的なシナリオを考えてみましょう。
-
3年前の類似記事よりも、昨日の記事が上位にランク付けされるニュース検索
-
30分のドライブが必要な場所よりも、5分圏内の場所を優先するレストラン検索
-
検索クエリとの類似度がわずかに低い場合でも、トレンド商品を上位表示するEコマースプラットフォーム
これらのシナリオはすべて、ベクトル類似度と、時間、距離、人気などの他の数値要素とのバランスを取るという共通のニーズを共有しています。
Zilliz CloudのDecay Rankerは、数値フィールド値に基づいて検索ランキングを調整することで、このニーズに対応します。これにより、ベクトル類似度とデータの「鮮度」、「近さ」、またはその他の数値プロパティとのバランスを取り、より直感的で文脈に関連性の高い検索エクスペリエンスを作成できます。
使用上の注意点
-
Decay Rankingは、グループ化検索では使用できません。
-
Decay Rankingに使用するフィールドは数値型である必要があります(
INT8、INT16、INT32、INT64、FLOAT、またはDOUBLE)。 -
各Decay Rankerは、1つの数値フィールドのみを使用できます。
-
時間単位の一貫性: 時間ベースのDecay Rankingを使用する場合、
origin、scale、およびoffsetパラメータの単位は、コレクションデータで使用されている単位と一致する必要があります。-
コレクションがタイムスタンプを秒で保存している場合、すべてのパラメータに秒を使用します。
-
コレクションがタイムスタンプをミリ秒で保存している場合、すべてのパラメータにミリ秒を使用します。
-
コレクションがタイムスタンプをマイクロ秒で保存している場合、すべてのパラメータにマイクロ秒を使用します。
-
仕組み
Decay Rankingは、時間や地理的距離などの数値要素をランキングプロセスに組み込むことで、従来のベクトル検索を強化します。プロセス全体は次のステージに従います。
ステージ 1: 正規化された類似度スコアの計算
まず、Zilliz Cloudはベクトル類似度スコアを計算し、一貫した比較を確実にするために正規化します。
-
L2およびJACCARD距離メトリックの場合(値が小さいほど類似度が高いことを示します):
normalized_score = 1.0 - (2 × arctan(score))/πこれにより、距離が0〜1の類似度スコアに変換され、スコアが高いほど良好であることを示します。
-
IP、COSINE、およびBM25メトリックの場合(スコアが高いほどすでに一致が良好であることを示します):スコアは正規化されずに直接使用されます。
ステージ2:減衰スコアの計算
次に、Zilliz Cloudは、選択した減衰ランカーを使用して、数値フィールド値(タイムスタンプや距離など)に基づいて減衰スコアを計算します。
-
各減衰ランカーは、生の数値値を0〜1の正規化された関連性スコアに変換します。
-
減衰スコアは、理想的な点からの「距離」に基づいてアイテムがどれだけ関連しているかを表します。
具体的な計算式は、減衰ランカーのタイプによって異なります。減衰スコアの計算方法の詳細については、ガウス減衰、指数減衰、線形減衰の専用ページを参照してください。
ステージ3:最終スコアの計算
最後に、Zilliz Cloudは正規化された類似度スコアと減衰スコアを組み合わせて、最終的なランキングスコアを生成します。
final_score = normalized_similarity_score × decay_score
ハイブリッド検索(複数のベクトルフィールドを組み合わせる)の場合、Zilliz Cloudは検索リクエストの中で正規化された類似度スコアの最大値を取ります。
final_score = max([normalized_score₁, normalized_score₂, ..., normalized_scoreₙ]) × decay_score
たとえば、ハイブリッド検索でベクトル類似度から0.82、BM25ベースのテキスト検索から0.91のスコアが得られた場合、Zilliz Cloudは減衰係数を適用する前に0.91を基本類似度スコアとして使用します。
減衰ランキングの動作例
時間ベースの減衰を伴う**「AI研究論文」**の検索という実用的なシナリオで、減衰ランキングを見てみましょう。
この例では、減衰スコアは関連性が時間とともにどのように減少するかを反映しています。新しい論文は1.0に近いスコアを受け取り、古い論文は低いスコアを受け取ります。これらの値は特定の減衰ランカーを使用して計算されます。詳細については、適切な減衰ランカーの選択を参照してください。
論文 | ベクトル類似度 | 正規化された類似度スコア | 発行日 | 減衰スコア | 最終スコア | 最終順位 |
|---|---|---|---|---|---|---|
論文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に正規化されます。
適切な減衰ランカーの選択
Zilliz Cloudは、特定のユースケース向けに設計された、gauss、exp、linearという異なる減衰ランカーを提供しています。
減衰ランカー | 特性 | 理想的なユースケース | シナリオ例 |
|---|---|---|---|
ガウス ( | 自然な感覚の緩やかな減少で、適度に広がる |
| レストラン検索では、3km離れた質の高い店も発見可能だが、近くの店よりも順位は低い |
指数 ( | 最初は急速に減少するが、長いテールを維持する |
| ニュースアプリでは、昨日の記事は1週間前のコンテンツよりもはるかに高い順位になるが、関連性の高い古い記事も表示されることがある |
線形 ( | 一貫性があり予測可能な減少で、明確なカットオフがある |
| イベント検索では、2週間以上先のイベントはまったく表示されない |
各減衰ランカーがどのようにスコアを計算し、特定の減少パターンを示すかについての詳細情報は、専用のドキュメントを参照してください。
実装例
減衰ランカーは、Zilliz Cloudの標準ベクトル検索とハイブリッド検索の両方の操作に適用できます。以下に、この機能を実装するための主要なコードスニペットを示します。
減衰関数を使用する前に、まず、減衰計算に使用される適切な数値フィールド(タイムスタンプ、距離など)を持つコレクションを作成する必要があります。コレクションのセットアップ、スキーマ定義、データ挿入を含む完全な動作例については、チュートリアル:Milvusで時間ベースのランキングを実装するを参照してください。
減衰ランカーの作成
減衰ランキングを実装するには、まず適切な設定でFunctionオブジェクトを定義します。
- Python
- Java
- NodeJS
- Go
- cURL
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
rerank = 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 rerank = 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 rerank = {
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パラメータに渡すことで、検索操作中に適用できます。
- Python
- Java
- NodeJS
- Go
- cURL
# 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=rerank, # 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(rerank)
.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: rerank,
consistency_level: "Strong",
});
// go
# restful