メインコンテンツまでスキップ

指数減衰

指数減衰は、検索結果において最初に急激な低下を生み出し、その後に長いテールを形成します。速報ニュースのサイクルのように、関連性は最初は急速に低下しますが、一部のストーリーは時間が経っても重要性を保ちます。指数減衰は、理想的な範囲をわずかに超えた項目に強いペナルティを適用しつつ、離れた項目も引き続き見つけられるようにします。このアプローチは、近接性や新しさを強く優先したい一方で、より離れた選択肢を完全には排除したくない場合に最適です。

他の減衰関数とは異なり、次のとおりです。

  • Gaussian decay は、より緩やかなベル型の減少を生み出します

  • Linear decay は、ちょうどゼロに達するまで一定の割合で減少します

指数減衰は、ペナルティを独自に「前倒し」で適用し、関連性の低下の大部分を早い段階で与えつつ、最小限ではあるもののゼロではない関連性の長いテールを維持します。

指数減衰を使うタイミング

指数減衰は、特に次のようなケースで効果的です。

ユースケース指数減衰が適している理由
ニュースフィード速報ニュースポータル古いニュースの関連性をすばやく下げつつ、数日前の重要記事も引き続き表示できます。
ソーシャルメディアのタイムラインアクティビティフィード、ステータス更新新しいコンテンツを強調しつつ、拡散した古いコンテンツも浮上させることができます。
通知システムアラートの優先順位付け最新のアラートに緊急性を持たせつつ、重要なアラートの可視性を維持します。
フラッシュセール期間限定オファー締切が近づくにつれて可視性を急速に下げます。

次のような場合は指数減衰を選択してください。

  • ユーザーが、ごく最近の項目や近い項目によって結果が強く支配されることを期待している場合

  • 古い項目やより遠い項目であっても、非常に関連性が高ければ引き続き発見可能であるべき場合

  • 関連性の低下を前倒しにしたい場合(最初は急で、後半はより緩やか)

急激な減衰の原理

指数減衰は、最初に急速に下がり、その後徐々に平坦になってゼロには到達しない長いテールへと近づく曲線を作ります。この数学的パターンは、放射性崩壊、人口減少、時間経過に伴う情報の関連性など、自然現象の中によく見られます。

📘Notes

すべての時間パラメータ(originoffsetscale)は、コレクションデータと同じ単位を使用する必要があります。コレクションが異なる単位(ミリ秒、マイクロ秒)でタイムスタンプを保存している場合は、すべてのパラメータをそれに合わせて調整してください。

YaRsbolv9oqomcxrFe5cXBa4nNg

上のグラフは、デジタルニュースプラットフォームにおいて、指数減衰がニュース記事のランキングにどのように影響するかを示しています。

  • origin(現在時刻):関連性が最大値(1.0)となる現在の時点です。

  • offset(3 時間):「速報ニュースのウィンドウ」—過去 3 時間以内に公開されたすべての記事は完全な関連性スコア(1.0)を維持するため、ごくわずかな時間差によって最新ニュースが不必要にペナルティを受けることはありません。

  • decay(0.5):scale 距離におけるスコア—このパラメータは、時間とともにスコアがどれだけ劇的に低下するかを制御します。

  • scale(24 時間):関連性が decay 値まで低下する時間期間—公開からちょうど 24 時間経過したニュース記事は、関連性スコアが半分(0.5)になります。

曲線からわかるように、24 時間を超えたニュース記事は関連性がさらに低下し続けますが、完全にゼロにはなりません。数日前の記事であっても、わずかな関連性は保たれるため、重要ではあるものの古いニュースもフィード内に表示され続けます(ただし順位は低くなります)。

この挙動は、ニュースの関連性が一般的にどのように機能するかを模倣しています。非常に新しい記事が強く優勢になる一方で、重要な古い記事も、ユーザーの関心に対して非常に高い関連性があれば、依然として上位に現れる可能性があります。

数式

指数減衰スコアを計算する数学的な式は次のとおりです。

S(doc)=exp(λmax(0,fieldvaluedocoriginoffset))S(doc) = \exp\left( \lambda \cdot \max\left(0, \left|fieldvalue_{doc} - origin\right| - offset \right) \right)

ここで、

λ=ln(decay)scale\lambda = \frac{\ln(decay)}{scale}

これを平易な言葉で分解すると、次のようになります。

  1. フィールド値が origin からどれだけ離れているかを計算します:fieldvaluedocorigin|fieldvalue_{doc} - origin|

  2. offset(存在する場合)を差し引きますが、ゼロ未満にはしません:max(0,distanceoffset)\max(0, distance - offset)

  3. それを λ\lambda に掛けます。λ\lambda は scale と decay パラメータから計算されます。

  4. 指数を取ることで、0 から 1 の間の値が得られます:exp(λvalue)\exp(\lambda \cdot value)

λ\lambda の計算は、scale と decay パラメータを指数関数のレートパラメータに変換します。λ\lambda がより負の値になるほど、初期の低下はより急になります。

指数減衰を使う

指数減衰は、Zilliz Cloud における標準のベクトル検索とハイブリッド検索の両方に適用できます。以下は、この機能を実装するための主要なコードスニペットです。

📘Notes

減衰関数を使用する前に、まず減衰計算に使用する適切な数値フィールド(タイムスタンプ、距離など)を持つコレクションを作成する必要があります。コレクションのセットアップ、スキーマ定義、データ挿入を含む完全な動作例については、Decay Ranker Tutorial を参照してください。

Decay Ranker を作成する

コレクションが数値フィールド(この例では publish_time)でセットアップされたら、指数減衰 ranker を作成します。

📘Notes

時間単位の一貫性:時間ベースの減衰を使用する場合、originscaleoffset の各パラメータがコレクションデータと同じ時間単位を使用していることを確認してください。コレクションがタイムスタンプを秒で保存している場合は、すべてのパラメータにも秒を使用してください。ミリ秒を使用している場合は、すべてのパラメータにもミリ秒を使用してください。

python
from pymilvus import Function, FunctionType
import datetime

# Create an exponential decay ranker for news recency
# Note: All time parameters must use the same unit as your collection data
rerank = Function(
name="news_recency", # Function identifier
input_field_names=["publish_time"], # Numeric field to use
function_type=FunctionType.RERANK, # Function type. Must be RERANK
params={
"reranker": "decay", # Specify decay reranker
"function": "exp", # Choose exponential decay
"origin": int(datetime.datetime.now().timestamp()), # Current time (seconds, matching collection data)
"offset": 3 * 60 * 60, # 3 hour breaking news window (seconds)
"decay": 0.5, # Half score at scale distance
"scale": 24 * 60 * 60 # 24 hours (in seconds, matching collection data)
}
)

decay ranker を定義した後、ranker パラメータに渡すことで検索操作中に適用できます。

python
# Apply decay ranker to 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=["title", "publish_time"], # Fields to return
ranker=rerank, # Apply the decay ranker
consistency_level="Strong"
)