Vector Retrieval Service for Milvus (Milvus) は、DiskANN インデックスでの範囲検索をサポートしています。DiskANN での範囲検索は Milvus の範囲検索セマンティクスに従います。クエリベクトルが中心となり、radius が外側境界を定義し、range_filter が内側境界を定義します。この帯域内のベクトルのみが保持され、最も類似した limit 件の結果が返されます。このトピックでは、これら 3 つのパラメーターの設定方法、search_list の値の選択方法、および期待される結果の解釈方法について説明します。
前提条件
-
DiskANN インデックスをサポートする Milvus インスタンスが作成済みであること。詳細については、「クイックスタート:Milvus インスタンスの作成」をご参照ください。
-
データを含むコレクションが準備され、検索対象のベクトルフィールドに DiskANN インデックスが構築済みであること。
-
インスタンスのエンドポイント、ユーザー名、パスワードが利用可能であり、Python を実行する環境からエンドポイントにアクセスできること。
-
PyMilvus がインストール済みであること。
pip install pymilvus
Alibaba Cloud の Milvus における DiskANN の実装とパラメーターは、オープンソース Milvus のものとは異なる場合があります。詳細については、「容量指向ディスクインデックス (DiskANN)」をご参照ください。小規模なデータセットを使用した例では、パラメーターと結果を説明します。検索ウィンドウのチューニングを評価するには、本番データと同等のサイズのデータセットを使用してください。
範囲検索の仕組み
radius を指定した検索は、3 つのステップで構成されます:
-
コレクションのメトリックを使用して、クエリベクトルとの類似度順 (最も近いものから最も遠いものへ) に候補ベクトルを検索します。DiskANN は、このステップでデータベース全体のブルートフォーススキャンではなく、グラフ検索を実行します。
-
距離またはスコアが
radiusとrange_filterの間に収まるベクトルのみを保持します。 -
保持されたベクトルのうち、最も類似した K 個を返します。K はリクエスト内の
limitの値です。
バンドに含まれるベクトルが K 個未満の場合、返される結果は K 個未満になることがあります。limit = 3 の場合、最大 3 つの結果が返されます。検索では、バンド内のすべてのベクトルが返されることは保証されません。
デフォルトでは、フィルタリングと検索の終了には、元のベクトルから計算された正確な距離を使用します。内側境界よりも近いベクトルを除外し、検索を続行します。バンド内のベクトルを保持します。検索は、K 件の結果が収集された時点、または次の正確な距離が外側境界に到達するか超えた時点で停止します。
メトリックと不等式
DiskANN は、L2、IP、および COSINE メトリックのみをサポートします。
距離が小さいほど類似度が高いメトリック (L2) の場合、外側境界の値は内側境界の値よりも大きくなります:
range_filter <= distance < radius
距離やスコアが大きいほど類似度が高くなるメトリック (IP や COSINE など) では、外側の境界値は内側の境界値よりも小さく、radius は range_filter よりも小さくする必要があります:
radius < distance <= range_filter
|
メトリック |
類似度の高さ |
バンド |
内側境界がない場合 |
|
L2 |
距離が小さい |
|
|
|
IP |
スコアが大きい |
|
|
|
COSINE |
スコアが大きい |
|
|
L2 のような、距離が小さいほど近傍が近いことを意味するメトリックでは、range_filter を 0 に設定すると半径内のすべてのエンティティが含まれます。COSINE および IP の場合、range_filter を 0 に設定すると区間は radius < d <= 0 となり、結果が返されない場合があります。たとえば、COSINE で radius = 0.4 かつ range_filter = 0 とした場合、正の類似度スコアを持つベクトルは除外されます。内側の境界は境界値を含み、外側の境界は境界値を除外します。L2 の場合、range_filter に等しい距離は保持され、radius に等しい距離は除外されます。IP および COSINE の場合、range_filter に等しいスコアは保持され、radius に等しいスコアは除外されます。
パラメーター
|
パラメーター |
目的 |
推奨事項 |
|
|
外側境界 |
メトリックと前の表に基づいて値を設定し、 |
|
|
内側境界。内側境界が不要な場合は、このパラメーターを省略します。 |
L2 については、このパラメーターを |
|
|
返す結果の最大数。検索終了条件の 1 つでもあります。 |
ビジネスで必要な結果数に K を設定します。この値を増やしても radius は拡大されません。 |
|
|
グラフ検索の候補ウィンドウ。検索の幅を制御します。 |
推奨値については、次のセクションをご参照ください。このパラメーターは、ページごとの結果数を指定するものではありません。 |
search_list は、返される結果の数を制限しません。limit = 500 および search_list = 100 の場合、検索は、バンド内で 500 件の結果が収集されるか、正確な距離が外部境界を超えるか、またはグラフ探索が終了するまで、グラフに沿って続行されます。100 番目の結果を返した後に停止しません。search_list が省略された場合、または 100 未満の値に設定された場合、範囲検索はウィンドウを 100 に調整します。返される結果の最大数は、引き続き limit です。
推奨される使用方法
リファインメントの有効化
範囲検索におけるフィルタリングと検索終了は、元のベクトルの距離に依存します。デフォルトのリファインメント設定を維持してください:
-
クエリパラメーターを
use_refine = falseに設定しないでください。
バンドの近傍深度に基づいて search_list を設定
候補ウィンドウは、ターゲットのバンドが存在する近傍深度をカバーする必要があります。以下のガイドラインに従ってください:
-
バンドに最近傍 (L2 で
range_filter = 0の場合、または IP/COSINE でrange_filterが省略された場合) が含まれる場合は、search_listを少なくともmax(100, limit)に設定します。limitが数十から 100 と小さい場合、100 のウィンドウを使用すると、標準的な TopK 検索に匹敵する再現率が得られます。 -
内側の境界が多くの最近傍を除外する場合、つまり
range_filterが多数の最も近いベクトルを除外し、ターゲットバンドがより遠くに位置する場合は、search_listをターゲット近傍の深さと同じオーダーに増やします。バンドが数千番目の最近傍あたりに位置する状況で、ウィンドウが 100 にすぎない場合、グラフ検索はバンドに到達できないことが多く、返す結果が少なくなります。
limit が search_list より大きい場合、結果は切り捨てられませんが、候補ウィンドウが比較的小さくなり、バンド内で K 番目の近傍に到達する確率が低下します。K 個の結果のリコールの安定性を向上させるには、search_list を K 以上に設定します。
limit による結果数の指定
limit は検索深度ではなく、返される結果の最大数を指定します。radius によって検索範囲が決定されると、limit の値を増やしても、そのバンド内のすべての結果が取得されるまで、同じバンドからより多くの結果が返されるだけです。そのバンドにベクトルが 10 個しか含まれていない場合、limit = 1000 としても、有効な結果は約 10 個しか得られません。PyMilvus は実際のヒットのリストを返し、リストを limit エントリまでパディングしません。
limit が 16384 以上の場合でも、範囲検索では大規模な TopK スキャンパスではなく、依然としてグラフ検索が使用されます。K が大きく、バンドが広いと、レイテンシーが大幅に増加します。数十件の結果のみが必要な場合は、limit にこのような大きな値を設定しないでください。
limit より少ない結果が返される場合があります
要求された数より少ない結果が返されるのは、次の 2 つの場合です:
-
バンド内のベクトルの数は
limit未満です。 -
正確な距離が外側境界を超え、後続の候補ベクトルがさらに遠くにある場合。
PyMilvus では、返されたヒットのリストを直接イテレートし、len(res[0]) を使用して実際のヒット数をカウントします。limit に基づいて固定数の結果スロットを予約する必要はありません。
グラフ検索がターゲットバンドをカバーしていない場合、返される結果が少なくなることもあります。この場合、半径を拡大せずに search_list を増やします。search_list がバンドの深さと同じオーダーの場合、再現率は同じウィンドウを持つ標準的な TopK 検索と同等の再現率になります。このウィンドウ内では、推定距離と正確な距離の間のわずかな順序の違いによって、区間条件を満たす結果がその条件から外れたり、結果のページ全体が常に欠落したりすることはありません。
例
以下の例では、それぞれ COSINE、L2、IP メトリックを使用する DiskANN インデックスを持つ既存のコレクション my_collection_cosine、my_collection_l2、my_collection_ip を使用します。ベクトルフィールド名は vector で、サンプルクエリベクトルは 8 次元です。エンドポイント、認証情報、コレクション名、ベクトルフィールド名、ベクトル次元を実際の値に置き換え、検索メトリックがインデックスメトリックと一致することを確認してください。
COSINE:スコアが 0.4 から 0.6 の間の上位 3 件の結果の取得
デフォルトのメトリックが COSINE の場合、radius は range_filter 未満である必要があります。 次のリクエストは、スコアが 0.4 < score <= 0.6 を満たす結果を最大 3 件、スコアの降順で返します。
from pymilvus import MilvusClient
client = MilvusClient(uri="http://<your-endpoint>:19530", token="<your-username>:<your-password>")
query_vector = [
0.3580376395471989,
-0.6023495712049978,
0.18414012509913835,
-0.26286205330961354,
0.9029438446296592,
0.0,
0.0,
0.0,
]
res = client.search(
collection_name="my_collection_cosine",
anns_field="vector",
data=[query_vector],
limit=3,
search_params={
"metric_type": "COSINE",
"params": {
"radius": 0.4,
"range_filter": 0.6,
"search_list": 100,
},
},
)
for hits in res:
for hit in hits:
print(hit["id"], hit["distance"])
limit = 3 の場合、search_list = 100 を使用できます。limit = 200 に変更する場合は、search_list を 200 以上に設定してください。
L2:内側境界なしで radius 100 内の上位 10 件の結果の取得
res = client.search(
collection_name="my_collection_l2",
anns_field="vector",
data=[query_vector],
limit=10,
search_params={
"metric_type": "L2",
"params": {
"radius": 100.0,
"range_filter": 0.0,
"search_list": 100,
},
},
)
0 <= distance < 100 を満たす最も近い結果を最大 10 件返します。
L2:近接する近傍を除外し、距離が 20 から 100 の間の結果を取得
res = client.search(
collection_name="my_collection_l2",
anns_field="vector",
data=[query_vector],
limit=10,
search_params={
"metric_type": "L2",
"params": {
"radius": 100.0,
"range_filter": 20.0,
"search_list": 100,
},
},
)
返される結果は 20 <= distance < 100 を満たします。距離が 20 未満の近傍が多数ある場合は、候補ウィンドウが内部境界によって除外された近傍深度をカバーするように、search_list を増やしてください。
IP:スコアが 0.2 から 0.8 の間の結果の取得
res = client.search(
collection_name="my_collection_ip",
anns_field="vector",
data=[query_vector],
limit=10,
search_params={
"metric_type": "IP",
"params": {
"radius": 0.2,
"range_filter": 0.8,
"search_list": 100,
},
},
)
0.2 < score <= 0.8 を満たす、スコアが最も高い結果を最大 10 件返します。
結果の検証
返される結果は、以下の条件を満たす必要があります:
-
L2:
range_filter <= distance < radius -
IP (内積) / コサイン:
radius < distance <= range_filter -
結果の数は
limitを超えません。 -
各クエリに対して、L2 の結果は距離の昇順でソートされ、IP / COSINE の結果はスコアの降順でソートされます。
limit より少ない結果が返された場合は、インターバルと search_list の設定を確認してください。この 2 つの要因だけでは、インターバル内のすべてのベクトルが返されたことを保証するものではありません。
注意事項
-
COSINE および IP では、
radiusとrange_filterの相対値が正しいことを確認してください。内部境界が不要な場合は、range_filterを 0 に設定するのではなく、省略してください。 -
search_listは、結果数ではなく、グラフ検索の検索候補の範囲を制御します。返される結果数は、limitとバンド内の実際のヒット数によって決まります。 -
範囲検索で
use_refine = falseを設定すると、推定距離を使用してバンドの境界がフィルタリングされ、境界付近の再現率が低下する可能性があります。 -
limitを増やしても、検索範囲は拡大されません。 -
範囲検索は、データベース内の範囲内にあるすべてのベクトルが返されることを保証するものではありません。DiskANN は、近似最近傍 (ANN) 検索を使用して、上位 K 個のベクトルを近いものから順に取得します。K および
search_listによって到達できなかった、より遠い近傍ベクトルは返されません。