ハイブリッド検索:テキストと画像を組み合わせて検索機能を強化
Milvusは、ハイブリッドスパース/デンスベクトル検索とマルチベクトル検索を可能にし、ベクトル化と検索プロセスを簡素化する。
ベクターデータベースの人気は驚くほど高まっている。しかし、ほとんどのベクトルデータベースは単一のベクトル列しかサポートし ておらず、より複雑なデータ型のアプリケーションへの利用を制限していました。Milvus 2.4は、疎密ベクトルを含む、同じデータベース内で複数のベクトル型をサポートすることで、状況を一変させました。この開発により、テキストや画像といった異なる形式のデータを単一のシステムで扱い、分析するための新たな可能性が開かれた。様々なデータ型を一箇所で扱う能力は、より効果的で効率的な検索・検索システムを開発する上で特に重要である。
このブログでは、Milvus 2.4が商品のような複雑なデータをベクターデータベースの1行で表現することを可能にする方法を探ります。このアプローチは、特にテキストと画像のようなハイブリッド検索を必要とするアプリケーションにおいて、ベクトル化と検索プロセスを簡素化します。
さっそく始めましょう。
スパースベクトルのまとめ
コード・スニペットに入る前に、スパース・ベクトルについて簡単に復習しておこう。
スパース・ベクトルは密なベクトルとは構造と使い方が異なります。密なベクトルは通常、ニューラルネットワークの最終層または最終層の活性化であり、通常は中程度の長さで、典型的なサイズは768、1024、または1536要素です。これらのベクトルでは、値のほとんどまたはすべてが非ゼロであり、比較的コンパクトな形式で多くの情報が含まれていることを意味します。
対照的に、スパース・ベクトルはその長さとスパース性が特徴です。ベクトルは長く、数万個の要素を含むことが多いが、非ゼロ要素はほとんどない。その構造上、スパース・ベクトルは、キーが非ゼロ要素のインデックス、値がこれらの要素の大きさである辞書として効率的に表現されます。
スパース・ベクトルは主に2つの文脈で使用される。1つ目は、TF-IDFやBM25などの伝統的な検索アルゴリズムで、検索クエリとの関連性に基づいて文書をスコアリングし、ランク付けする方法です。これらのアルゴリズムでは、与えられた文書やクエリに関連する用語は語彙全体から見てわずかであるため、疎なベクトルが生成される。2つ目のコンテキストは、SPLADEのような学習済みモデルで、テキストや他のタイプのデータを表現するために機械学習技術によってスパースベクトルを生成する。これらのモデルは、必ずしもテキストに現れない隣接または関連するトークンの重要性を学習することができ、すべての関連キーワード/クラスを捕捉する「学習された」スパース表現につながります。
Milvusにおけるハイブリッド検索の仕組み
Milvus 2.4以前では、スパース検索とデンスベクトル検索の結果を組み合わせるには、通常何らかのレイトフュージョンが必要でした。複数の密なベクトルの場合、これは2つの異なるコレクションの検索結果を「結合」することで行われましたが、意味+語彙のハイブリッドなテキスト検索アプリケーションの場合、これはLuceneのような別のシステムを必要としました。 Milvus 2.4は、このような状況を一変させ、単一システムでスパースベクター検索とハイブリッド検索を可能にした。Milvus 2.4は、指定されたベクトル列に対する個別の検索要求を作成し、その結果にランク関数を適用する。Milvusは現在、基本的な重み付き平均と相互ランクフィルタリングをサポートしている。
# ハイブリッド検索は多くの列を横断するので、最初に個別の検索要求を作成する必要があります。
req_0 = AnnSearchRequest(query_vector_0, "vector_col_0", search_params_0, limit=topk)
req_1 = AnnSearchRequest(query_vector_1, "vector_col_1", search_params_1, limit=topk)
# これら2つの検索リクエストが揃ったので、次はこれらをpymilvusのハイブリッド検索関数に投入します。
reqs = [req_0, req_1]
res = col.hybrid_search(reqs, rerank=RRFRanker(), limit=topk)
上の例はMilvusで多ベクトル検索を実行する方法を示している。2つの列(vector_col_0 または vector_col_1)のどちらかをスパースまたは密な浮動小数点ベクトルとして指定することができる。検索要求が整うと、それらをMilvusに送って評価させることができる。
今すぐには理解できなくても心配しないでほしい。今すぐ2つの例を調べてみよう。
ハイブリッド検索の例
Milvusベクトルデータベース](https://zilliz.com/what-is-milvus)のスパース+密ベクトル複合検索の例を見てみましょう。まず、Milvus 2.4のインスタンスを立ち上げるところから始めます:
!wget https://github.com/milvus-io/milvus/releases/download/v2.4.0-rc.1/milvus-standalone-docker-compose-gpu.yml -O docker-compose.yml
docker compose up -d
docker ps -a
名前 コマンド 状態 ポート
--------------------------------------------------------------------------------------------------------------------
milvus-etcd etcd -advertise-client-url ... アップ 2379/tcp, 2380/tcp
milvus-minio /usr/bin/docker-entrypoint ... アップ (健全) 9000/tcp
milvus-standalone /tini -- milvus run standalone Up 0.0.0.0:19530->19530/tcp, 0.0.0.0:9091->9091/tcp
必ずMilvusの最新バージョン(執筆時点では2.4.0-rc1)を使用すること。
では、適切なライブラリをセットアップしよう。pymilvus[models]`の中にあるBGE-M3を使って、上記のテキストに対してスパースベクトルとデンスベクトルの両方を生成する。この投稿ではBGE-M3の詳細にはあまり触れません。もし、より本質的なことに興味があれば、BGE-M3 paperをご覧ください。
!pip install -U pymilvus
!pip install -U pymilvus[モデル].
いつものように、開始する前に virtualenv を使用していることを確認してください。それでは、インポートをセットアップしましょう:
np として numpy をインポートする。
from pymilvus import (
ユーティリティ
FieldSchema, CollectionSchema, DataType、
Collection, AnnSearchRequest, RRFRanker, connections、
)
from pymilvus.model.hybrid import BGEM3EmbeddingFunction
ここからは、"データセット "を3つの文のリストとして定義します。また、BGE-M3を使って、クエリテキストだけでなく、ドキュメントの疎埋め込みと密埋め込みを生成します:
docs = [
「人工知能は1956年に学問分野として設立された、
"アラン・チューリングはAIの実質的な研究を行った最初の人物である"、
「チューリングはロンドンのマイダベールで生まれ、イングランド南部で育った、
]
query = "AI研究を始めたのは誰ですか?"
ef = BGEM3EmbeddingFunction(use_fp16=False, device="cpu")
dense_dim = ef.dim["dense"].
docs_embeddings = ef(docs)
query_embeddings = ef([query])
このステップで、データを保持するコレクションを作成できます。特に、スキーマで2つのベクトルカラムを指定します。1つは sparse_vector で、もう1つは dense_vector です。また、この2つのカラムに対してインデックスを作成する;
connections.connect("default", host="localhost", port="19530")
fields = [
# 自動生成されたidを主キーとして使用する
フィールドスキーマ(name="pk", dtype=DataType.VARCHAR、
is_primary=True, auto_id=True, max_length=100)、
FieldSchema(name="text",dtype=DataType.VARCHAR,max_length=512)、
FieldSchema(name="sparse_vector", dtype=DataType.SPARSE_FLOAT_VECTOR)、
フィールドスキーマ(name="dense_vector"、dtype=DataType.FLOAT_VECTOR、dim=dense_dim)。
]
schema = CollectionSchema(fields, "")
col = Collection("sparse_dense_demo", schema)
sparse_index = {"index_type":"SPARSE_INVERTED_INDEX", "metric_type":"IP"}。
dense_index = {"index_type":"FLAT", "metric_type":"COSINE"}。
col.create_index("sparse_vector", sparse_index)
col.create_index("dense_vector", dense_index)
ステータス(code=0, message=)
ここから、Milvusに文書を挿入することができる:
``python entities = [docs, docs_embeddings["sparse"], docs_embeddings["dense"]]. col.insert(entities) col.flush()
クエリーはもう少し複雑だ。Milvusから返されたスパースベクトルとデンスベクトルを協調ランク付けする方法を指定しなければならない。この例では、各メソッドの相互順位に基づいてドキュメントをランク付けする相互ランクフュージョンを使用します。つのカラムに対して2つの検索リクエストを作成し、`RRFRanker`を用いてそれらをマージする:
sparse_req = AnnSearchRequest(query_embeddings["sparse"]、 "sparse_vector", {"metric_type":"IP"}, limit=2) dense_req = AnnSearchRequest(query_embeddings["dense"]、 "dense_vector", {"metric_type":"COSINE"}, limit=2)
res = col.hybrid_search([sparse_req, dense_req], rerank=RRFRanker()、 limit=2, output_fields=["text"])
['["id:448695871357611268, distance:0.032786883413791656, entity:{'text':\Alan Turing was the first person to conduct substantial research in AI.}", "id:448695871357611267, distance:0.016129031777381897, entity:{text':\人工知能は1956年に学問分野として創設された。]
以上である。数百万、数十億のテキスト文書に簡単に拡張できる小さなデータセットで実験した。
この例はpymilvusの例と一緒にブートキャンプにあります。
## シングルモダリティ検索を超えて
純粋なテキスト検索は素晴らしいものですが、いくつかの明らかな限界があります。特に、ほとんどの非構造化データは複数のモダリティで表現されます。推薦システムでよく使われる非構造化データの一種である商品を例にとってみよう。商品には、画像、説明、レビューなど多くの属性があります。各属性はベクトル化され、ベクトルデータベースの中で1つの「行」を形成するように結合されて保存されます。例えば、商品はその画像から得られる密なベクトルと、テキストの説明から得られる疎なベクトルの両方によって表現することができます。
Kaggleの[2023 Amazon Product Dataset](https://www.kaggle.com/datasets/asaniczka/amazon-products-dataset-2023-1-4m-products)を使った例を見てみよう([my Google Drive](https://drive.google.com/file/d/1DVb8WcwxD6polcReqxmR0BA6Yr0-mW-h/view?usp=sharing)からもダウンロードできる)。このデータセットでは、各商品の最初の画像を密なベクトルに、商品のタイトルを疎なベクトルに変換することができる。まずデータセットをpandasのデータフレームにロードしましょう:
pandas を pd としてインポートする
df = pd.read_csv("amazon_products.csv") df = df[df["title"].str.len() > 0]. df.columns
Index(['asin', 'title', 'imgUrl', 'productURL', 'stars', 'reviews', 'price'、
'listPrice', 'category_id', 'isBestSeller', 'boughtInLastMonth']、
dtype='object')
画像から密なベクトルを作成するために[GResNet](https://frankzliu.com/blog/vision-transformers-are-overrated)を採用する。
from collections import OrderedDict
インポートトーチ import torch.nn as nn from torchvision.models import gresnet50
gresnet = gresnet50() weights = torch.load("model_426.pth", map_location=torch.device("cpu"))["model_ema"]. weights_ = OrderedDict() for (k, w) in weights.items(): if k != "n_averaged": weights_[k.replace("module.", "")] = w gresnet.load_state_dict(weights_) gresnet.eval() gresnet_transforms = partial(ImageClassification, crop_size=224, resize_size=232)() gresnet.classifier[2] = nn.Identity()
ここでは、簡単のために省略した画像をダウンロードするための別のコードがあります。
商品の説明については、TF-IDFを使うことができます。TF-IDFは、文書の集まり(コーパス)に対する文書内の単語の重要度を反映する手法です。TF-IDFの用語頻度部分(TF)は特定の文書における単語の頻度を測定し、逆文書頻度部分(IDF)はすべての文書にわたる単語の希少性を評価する:
from sklearn.feature_extraction.text import TfidfVectorizer
vectorizer = TfidfVectorizer() X = vectorizer.fit_transform(df["title"]) X
<クラス 'numpy.float64'>' 型の 1426336x429087 の疎行列。
圧縮されたスパース行形式で23340037個の要素が格納されています。
次に、ベクトル用の2つの列とともにコレクションを作成し、それらをMilvusに挿入し、上記のようにハイブリッド検索を実行することができる。これにより、既存ユーザーが既に購入した "クエリ商品 "から類似商品を検索することができる:
res = col.hybrid_search([sparse_req, dense_req], rerank=RRFRanker()、 limit=10, output_fields=["productURL"])
これで完了だ!この2つのベクトル(画像は密に、テキストは疎に)を統合することで、各商品の視覚的要素とテキスト要素の両方を捉えた全体的な表現を作り出している。この例は、シングルモダリティ検索を超え、ハイブリッドモデルを活用することで、検索システムの効率と効果を大幅に改善できることを示している。
商品画像とテキストの挿入(およびクエリ)のためのハイレベルフローチャート](https://assets.zilliz.com/High_level_flowchart_for_insertions_and_queries_of_product_images_and_text_20240410_110842_d56ac5b027.png)
## まとめ
この投稿では、Milvus 2.4におけるハイブリッド検索の実用的な応用について、2つの実践的な例を通して探ってきた。最初の例では、1つのテキストをスパースとデンスの両方のベクトルフィールドで表現し、それぞれの長所を活かして検索機能を強化する方法を紹介した。2つ目の例では、非構造化データ(特に製品)の異なるコンポーネントを、画像用の密なベクトルとテキスト記述用の疎なベクトルという別々のベクトルとして表現する方法を実演しました。
テキストと画像を組み合わせるこのアプローチは、今後非常に一般的になるでしょうが、この2つのモダリティに限定されるわけではありません。異なるフォーマットで保存された複数のモダリティのデータを、それぞれのフォーマットがベクトル・データベースの異なるカラムに存在することは容易に想像できる。
Frank LiuDirector of Operations & ML Architect at ZillizFrank Liu is the Director of Operations & ML Architect at Zilliz, where he serves as a maintainer for the Towhee open-source project. Prior to Zilliz, Frank co-founded Orion Innovations, an ML-powered indoor positioning startup based in Shanghai and worked as an ML engineer at Yahoo in San Francisco. In his free time, Frank enjoys playing chess, swimming, and powerlifting. Frank holds MS and BS degrees in Electrical Engineering from Stanford University.





