Gaussian Decay
Gaussian decay(正規減衰とも呼ばれます)は、検索結果に対して最も自然に感じられる調整を行います。距離が離れるにつれて徐々にぼやける人間の視覚のように、Gaussian decay は、アイテムが理想的なポイントから離れるにつれて関連性をやさしく低下させる、滑らかなベル型の曲線を作り出します。このアプローチは、好ましい範囲をわずかに外れたアイテムに厳しくペナルティを与えることなく、それでいて遠く離れたアイテムの関連性はしっかり下げたい場合に最適です。
他の decay ranker とは異なり、以下の特徴があります。
-
Exponential decay は最初に急激に低下し、より強い初期ペナルティを生み出します
-
Linear decay はゼロに達するまで一定の割合で減少し、明確なカットオフを作ります
Gaussian decay は、ユーザーにとって自然に感じられる、よりバランスの取れた直感的なアプローチを提供します。
Gaussian decay を使用するタイミング
Gaussian decay は特に次のようなケースで効果的です。
| ユースケース | 例 | Gaussian が適している理由 |
|---|---|---|
| 位置ベースの検索 | レストラン検索、店舗検索 | 距離と関連性に対する人間の自然な知覚を再現できる |
| コンテンツ推薦 | 公開日に基づく記事の提案 | コンテンツが古くなるにつれて関連性が徐々に低下する |
| 商品一覧 | 目標価格に近いアイテム | 価格が目標から外れるにつれて関連性が滑らかに低下する |
| 専門性のマッチング | 関連する経験を持つ専門家の検索 | 経験の関連性をバランスよく評価できる |
厳しいペナルティや明確なカットオフなしに、自然な関連性の低下を必要とするアプリケーションでは、Gaussian decay が最適な選択肢となる可能性が高いです。
ベルカーブの原理
Gaussian decay は、理想的なポイントからの距離が大きくなるにつれて関連性を徐々に低下させる、滑らかなベル型の曲線を作り出します。この分布は数学者 Carl Friedrich Gauss にちなんで名付けられ、自然界や統計に頻繁に現れるため、人間の知覚にとって非常に直感的に感じられます。

上のグラフは、モバイル検索アプリにおけるレストランのランキングに Gaussian decay がどのように影響するかを示しています。
-
origin(0 km): 現在地であり、関連性が最大値(1.0)になる場所です。 -
offset(±300 m): 現在地の周囲にある「満点ゾーン」です。300 メートル以内にあるすべてのレストランが完全な関連性スコア(1.0)を維持することで、非常に近い候補がわずかな距離差によって不必要にペナルティを受けないようにします。 -
scale(±2 km): 関連性が decay 値まで低下する距離です。ちょうど 2 キロメートル離れたレストランの関連性スコアは半分(0.5)になります。 -
decay(0.5): scale 距離におけるスコアです。このパラメーターは本質的に、距離に応じてスコアがどれだけ速く低下するかを制御します。
曲線からわかるように、2 km を超えたレストランは関連性が引き続き低下しますが、完全にゼロになることはありません。4~5 キロメートル離れたレストランであっても最小限の関連性は維持されるため、優れていても遠いレストランも検索結果に表示され続けます(ただし順位は低くなります)。
この挙動は、距離と関連性について人が自然に考える方法を模倣しています。近くの場所が好まれる一方で、優れた選択肢のためなら遠くまで移動することも厭いません。
数式
Gaussian decay スコアを計算する数式は次のとおりです。
ここで、
これを平易な言葉で分解すると、次のようになります。
-
フィールド値が origin からどれだけ離れているかを計算します:
-
offset(存在する場合)を差し引きますが、ゼロを下回ることはありません:
-
この調整後の距離を二乗します:
-
これを $2\sigma^2$ で割ります。これは scale と decay のパラメーターから計算されます
-
負の指数を取ると、0 から 1 の間の値が得られます:
の計算は、scale と decay のパラメーターをガウス分布の分散(標準偏差の二乗)に変換します。これにより、この関数に特徴的なベル型の形状が得られます。
Gaussian decay を使用する
Gaussian decay は、Zilliz Cloud における標準ベクトル検索とハイブリッド検索の両方に適用できます。以下に、この機能を実装するための主要なコードスニペットを示します。
decay 関数を使用する前に、まず decay 計算に使用する適切な数値フィールド(タイムスタンプ、距離など)を持つコレクションを作成する必要があります。コレクションのセットアップ、スキーマ定義、データ挿入を含む完全な動作例については、チュートリアル: Milvus で時間ベースのランキングを実装する を参照してください。
decay ranker を作成する
コレクションに数値フィールド(この例では、ユーザーからの距離をメートル単位で表す distance)を設定したら、Gaussian decay ranker を作成します。
- Python
- Java
- NodeJS
- Go
- cURL
- C++
from pymilvus import Function, FunctionType
# Create a Gaussian decay ranker for location-based restaurant search
rerank = Function(
name="restaurant_distance_decay", # Function identifier
input_field_names=["distance"], # Numeric field for distance in meters
function_type=FunctionType.RERANK, # Function type. Must be RERANK
params={
"reranker": "decay", # Specify decay reranker
"function": "gauss", # Choose Gaussian decay
"origin": 0, # Your current location (0 meters)
"offset": 300, # 300m no-decay zone
"decay": 0.5, # Half score at scale distance
"scale": 2000 # 2 km scale (2000 meters)
}
)
import io.milvus.v2.service.vector.request.ranker.DecayRanker;
DecayRanker rerank = DecayRanker.builder()
.name("restaurant_distance_decay")
.inputFieldNames(Collections.singletonList("distance"))
.function("gauss")
.origin(0)
.offset(300)
.decay(0.5)
.scale(2000)
.build();
import { FunctionType } from "@zilliz/milvus2-sdk-node";
const rerank = {
name: "restaurant_distance_decay",
input_field_names: ["distance"],
function_type: FunctionType.RERANK,
params: {
reranker: "decay",
function: "gauss",
origin: 0,
offset: 300,
decay: 0.5,
scale: 2000,
},
};
// go
# restful
auto rerank = std::make_shared<milvus::DecayRerank>("restaurant_distance_decay");
rerank->AddInputFieldName("distance");
rerank->SetFunction("gauss");
rerank->SetOrigin(0);
rerank->SetScale(2000);
rerank->SetOffset(300);
rerank->SetDecay(0.5);
標準ベクトル検索に適用する
decay ranker を定義した後は、検索操作時に ranker パラメーターへ渡すことで適用できます。
- Python
- Java
- NodeJS
- Go
- cURL
- C++
# Apply decay ranker to restaurant vector search
result = milvus_client.search(
collection_name,
data=[your_query_vector], # Replace with your query vector
anns_field="dense", # Vector field to search
limit=10, # Number of results
output_fields=["name", "cuisine", "distance"], # Fields to return
ranker=rerank, # Apply the decay ranker
consistency_level="Strong"
)
import io.milvus.v2.common.ConsistencyLevel;
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("italian restaurants")))
.annsField("vector_field")
.limit(10)
.outputFields(Arrays.asList("name", "cuisine", "distance"))
.functionScore(FunctionScore.builder()
.addFunction(rerank)
.build())
.consistencyLevel(ConsistencyLevel.STRONG)
.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: ["name", "cuisine", "distance"],
rerank: rerank,
consistency_level: "Strong",
});
// go
# restful
auto function_score = std::make_shared<milvus::FunctionScore>();
function_score->AddFunction(rerank);
auto request = milvus::SearchRequest()
.WithCollectionName(collection_name)
.WithAnnsField("dense")
.WithRerank(function_score)
.WithLimit(10)
.AddOutputField("name")
.AddOutputField("cuisine")
.AddOutputField("distance")
.AddFloatVector(your_query_vector)
.WithConsistencyLevel(milvus::ConsistencyLevel::STRONG);
milvus::SearchResponse response;
auto status = client->Search(request, response);
if (!status.IsOk()) {
std::cout << status.Message() << std::endl;
}