プロダクションにおける超高速意味的類似性検索
CLIPエンベッディングと最先端のオープンソース・ベクターデータベースMilvusを使った、高速でスケーラブルなテキスト画像検索の構築。
シリーズ全体を読む
- 画像ベースの商標類似検索システム:知的財産権保護のよりスマートなソリューション
- HM-ANN 効率的なヘテロジニアスメモリ上の10億点最近傍探索
- ベクトル類似度検索でワードローブを持続可能にする方法
- 近接グラフに基づく近似最近傍探索
- 画像類似性検索でオンラインショッピングをよりインテリジェントにするには?
- グラフィカル・デザイナーのための知的類似性検索システム
- ベクトル類似性検索にフィルタリングをベストフィットさせるには?
- ベクトル類似性検索によるインテリジェントなビデオ重複排除システムの構築
- 最先端の埋め込みを用いたコンピュータビジョンにおける意味的類似性検索の強化
- プロダクションにおける超高速意味的類似性検索
- ベクトル・インデックスによるビッグデータ上の類似検索の高速化(後編)
- ニューラルネットワークの埋め込みを理解する
- 機械学習をアプリケーション開発者により身近なものに
- ベクターデータベースによる対話型AIチャットボットの構築
- 2024年のプレイブックベクトル検索のトップユースケース
- ベクター・データベースの活用による競合他社のインテリジェンス強化
- ベクターデータベースでIoT分析とデバイスデータに革命を起こす
- 推薦システムとベクターデータベース技術の利用について知っておくべきすべて
- ベクターデータベースでスケーラブルなAIを構築する:2024年の戦略
- アプリの機能強化:ベクターデータベースによる検索の最適化
- リスクと不正分析のための金融におけるベクトル・データベースの応用
- ベクターデータベースによる顧客体験の向上:戦略的アプローチ
- PDFをインサイトに変換:Zilliz Cloud Pipelinesによるベクトル化と取り込み
- データの保護ベクターデータベースシステムにおけるセキュリティとプライバシー
- ベクターデータベースを既存のITインフラと統合する
- 医療を変える:患者ケアにおけるベクター・データベースの役割
- ベクターデータベースによるパーソナライズされたユーザー体験の創造
- 予測分析におけるベクトル・データベースの役割
- ベクターデータベースでコンテンツ発見の可能性を引き出す
- ベクターデータベースを活用した次世代Eコマース・パーソナライゼーション
- Zilliz Cloudでベクトルを使ったテキスト類似検索をマスターする
- ベクターデータベースによる顧客体験の向上:戦略的アプローチ
#はじめに
前回の投稿](https://zilliz.com/learn/embedding-generation)で、最先端のニューラルネットワークを使って、画像を類似検索用の埋め込みに変換する3つの簡単な方法を紹介した。この投稿では、これらの埋め込みを、最も人気のあるオープンソースのベクトル検索データベースの一つであるMilvusとどのように組み合わせれば、プロダクションスケールのText-to-Image検索アプリケーションを作成できるかについて説明します。具体的には、この投稿では以下のことを取り上げます。
1.プロダクション・スケールのベクトル類似検索に関する考察 2.環境のセットアップとデータのダウンロード 3.Milvusを使ったテキストから画像への類似検索アプリケーションの実装
この投稿では、KaggleのH&M Personalized Fashion Recommendations competitions datasetを使って、拡張性の高いテキストから画像へのeコマース商品検索サービスを構築します。このデータセットのライセンスはNon-Commercial Purposes & Academic Research licenseです。
プロダクションスケールのベクトル類似検索のための考察
以前の投稿](https://zilliz.com/learn/embedding-generation)では、クエリ画像のベクトルと他のすべての画像ベクトルとの余弦類似度を計算することで、類似画像を発見していました。この方法は、その投稿で使用した小規模なデータセットではうまくいきましたが、実運用規模の作業負荷に拡張すると問題が発生します。
はっきりさせるために、私が本番のワークロードについて言及するとき、私は実際の電子商取引アプリケーションについて話している。
1.数千万から数億、あるいは数十億の画像の検索スペース。 2.毎秒数千の検索クエリー 3.各クエリの検索スピードは秒以下
すべてのクエリで数百万の画像ベクトルのデータベース全体をスキャンする必要がある場合、秒以下のレイテンシーで結果を返す費用対効果の高い方法はありません。ではどうすればいいのか?
近似最近傍探索
私の記事KNN is Dead!では、網羅的なペアワイズ距離計算を使って最近傍を見つけることは非常に非効率的で時間がかかりすぎることを紹介し、経験的に証明しました。その代わりに、類似性検索を大容量のデータに拡張するための解決策は、総当たり距離計算を完全に回避し、その代わりに近似最近傍(ANN)と呼ばれるより洗練されたアルゴリズムのクラスを使用することです。
ANNアルゴリズムは、すべての画像ベクトルをANNインデックスデータ構造にロードすることで動作し、高速で効率的な最近傍探索を可能にします。ANNインデックスには、量子化ベース、グラフベース、ツリーベース、ハッシュベースなどの種類があります。様々なANNインデックスに関する包括的なガイドと、それぞれのインデックスを使用する際の推奨事項については、Milvusのドキュメントをご覧ください。
Milvusがサポートするインデックス](https://assets.zilliz.com/index_4109de98dd.png)
いくつかのオープンソースライブラリが上記の様々なANNインデックスの実装を提供している。ほとんどのライブラリは、単一のマシンのみで動作するか、単一の ANN インデックスの実装に限定されている。本番規模のベクトル類似性検索アプリケーションを真に実行するためには、我々のニーズに最も適したANNインデックスを選択する選択肢も提供する、堅牢なベクトル検索データベースが必要である。Milvusはそのようなデータベースである!
Milvus
Milvusは、スケーラブルな類似検索のために構築されたオープンソースのベクトルデータベースです。データサイエンティストの視点から見ると、Milvusは以下のような魅力的な利点を提供します:
1.1.Milvusはローカルでもクラウド上のクラスタでも簡単にインストールできる。 2.Milvusは、FAISSインデックス、HNSW、ANNOYなどを含む最も一般的な10種類のANNインデックスを提供します。 3.Milvusはまた、IP、L2、ハミング等を含む7種類の類似性メトリクスを提供します。 4.スカラーデータを格納し、スカラーフィルタリングとベクトル類似性検索を組み合わせたハイブリッド検索を行う。 5.Python](https://milvus.io/docs/v2.0.x/install-pymilvus.md)、Java、Go、Node.jsの高水準SDK。 6.Attu](https://zilliz.com/attu)と呼ばれる対話型GUIクライアント。 7.ディープラーニングモデルからのベクトルに対して、その類似性検索は非常に高速である!
Milvusとそのアーキテクチャの詳細については、Milvus概要ドキュメントをご覧ください。それでは、Milvusを使ってテキストから画像への類似検索アプリケーションを実装してみましょう。
環境の構築とデータのダウンロード
前回の投稿](https://zilliz.com/learn/embedding-generation)では、OpenAIのCLIP (Contrastive Language-Image Pre-Training)を使って、Image-to-TextやText-to-Imageのマルチモーダル検索のための埋め込みデータを生成する方法を紹介しました。言い換えれば、検索テキストクエリが与えられたとき、画像とテキストの両方を同じベクトル空間に埋め込むことで、このテキストクエリにマッチするトップ画像を直接見つけることができる。この投稿では、10万枚の画像からなる大規模なデータセットを含む、テキストから画像への類似検索アプリケーションを実装する。Milvusの素晴らしい点は、同じ手順でこのアプリケーションを数億、数十億の画像に拡張できることです!
環境のセットアップ
まず、Milvusのセットアップをテストするために、いくつかの画像データをロードする必要があります。今回は、KaggleのH&M Personalized Fashion Recommendations competitions datasetを使ってみよう。このデータセットには、100K以上のeコマース商品の画像が含まれており、テキストから画像への商品検索サービスをテストするのに最適だ。私の前回の投稿と同様に、このプロジェクトの仮想環境を管理するためにAnacondaを使用する。以下のコードは、埋め込み生成に関するこのブログで言及したKaggle環境のセットアップを既に行っていることを前提としています。もしセットアップしていないのであれば、先に進む前にその投稿にアクセスしてセットアップしてください。
# 必要なディレクトリを作成する。
mkdir -p milvus_image_search/notebooks milvus_image_search/data milvus_image_search/milvus
# CDをデータディレクトリに入れる
cd milvus_image_search/data
# conda 環境を作成し、有効化する。
conda create -n image_sim python=3.8 -y
conda activate image_sim
## conda を使っていない場合は venv を使って仮想環境を作成する # # python -m venv image_sim
# python -m venv image_sim
# ソース image_sim/bin/activate
# Pip で必要なライブラリをインストールする
pip install jupyterlab kaggle matplotlib scikit-learn tqdm ipywidgets
pip install pandas==1.3.5 pymilvus==2.0.0
pip install sentence_transformers ftfy
# kaggle APIを使ってデータをダウンロードする
kaggle competitions download -c h-and-m-personalized-fashion-recommendationsをダウンロードする。
# データをローカルディレクトリに解凍する
h-and-m-personalized-fashion-recommendations.zip を解凍する。
# Zipファイルを削除する
rm h-and-m-personalized-fashion-recommendations.zipを削除する。
データをPythonに読み込む
milvus_image_search/notebooks`ディレクトリに新しいjupyter notebookを作成し、埋め込みデータを生成してMilvusにアップロードしよう。まず、必要なライブラリをインポートする。
python from matplotlib import pyplot as plt import pandas as pd from pathlib import Path from PIL import Image from sklearn.preprocessing import normalize import time from tqdm import tqdm
ダウンロードされた画像はいくつかのサブフォルダに分かれている。すべての画像のパスを見つけ、これらのパスを対応する商品 `article_id` と共に pandas dataframe に格納しましょう。形状から、このデータセットに10万5千の画像パスがあることがわかる!
python
# ダウンロードされた画像へのパス
img_path = Path('../data/images')
# パス内の全ファイルのリストを検索
images = [path for path in img_path.glob('**/*.jpg')] # パス内の全ファイルのリストを検索する。
# ファイル名をデータフレームに読み込む
image_df = pd.DataFrame(images, columns=['img_path'])
image_df['article_id'] = image_df['img_path'].apply(lambda x: int(x.stem))
print(image_df.shape)
image_df.head()
データセット内の10万5千の画像パス
次に、以下のように、商品の article_id
を img_path
と prod_name
に Python 辞書でマッピングしてみましょう。この product_mapping
辞書は、後で検索結果を視覚化するときに便利です。
``python
商品名を含む商品マッピング辞書を作成する。
articles_df = pd.read_csv('../data/articles.csv') product_mapping = image_df.merge(articles_df[['article_id', 'prod_name']]、 on='article_id')
product_mapping = product_mapping.set_index('article_id') product_mapping = product_mapping.to_dict(orient='index')
print(product_mapping[554541045])
商品マッピング](https://assets.zilliz.com/Untitled_1_a0955c9a68.png)
これでMilvusを使い始めるのに必要なものはすべて揃った。
## Milvusを使ったテキストから画像への類似検索アプリケーションの実装
Milvusを使用する際の一般的な開発ワークフローは以下の通りです。
1.ターミナルからMilvusサーバを起動する。
2.Milvusコレクションを作成する
3.エンベッディングを一括生成し、Milvusコレクションにアップロードする。
4.アップロードされたデータにANNインデックスを作成する。
5.クエリーを実行する!
これらのステップを詳しく見てみよう。
### ターミナルからMilvus Serverを起動する。
今回はローカルマシンでのみMilvusを動作させるため、[Milvus Standalone](https://milvus.io/docs/install_standalone-docker.md)を使用する。MilvusのインストールはDockerを使えば簡単なので、まずは以下をインストールします:
1.Docker公式サイト](https://docs.docker.com/engine/install/)の指示に従ってDockerをインストールする。
2.Docker-compose公式ページ](https://docs.docker.com/compose/install/)を参考に、Docker Composeをインストールする。
Dockerのインストールが完了したので、Milvusスタンドアロンサーバのインストールと起動は、以下のコードにあるように、`docker-compose.yml`をダウンロードし、dockerコンテナを起動するだけでよい。docker-composeを使用することで、プロセス全体が非常にシンプルかつ効率的になるでは、MilvusスタンドアロンとMilvusクラスタの両方をインストールするための他の多くのオプションを提供しています。Kubernetesクラスタにインストールする必要がある場合、またはオフラインでインストールする必要がある場合など、ぜひチェックしてみてください。
``bash
# CD から milvus ディレクトリに移動する。
cd ../milvus
# docker-compose.yml をダウンロードする。
wget https://github.com/milvus-io/milvus/releases/download/v2.0.0/milvus-standalone-docker-compose.yml -O docker-compose.yml
# milvusスタンドアロンサーバーを起動する
sudo docker-compose up -d
Milvusコレクションの作成
ローカルマシンでMilvusサーバーが動作するようになったので、pymilvus
ライブラリを使ってMilvusサーバーとやりとりすることができる。まず、必要なモジュールをインポートし、localhost
上で動作しているMilvusサーバにセッションを接続してみよう。パラメータ alias
と collection_name
は自由に変更して構わない。emb_dim`パラメータの値は、画像やテキストを埋め込みデータに変換する際に使用するモデルによって決定される。CLIPの場合、エンベッディングは512dである。
python
Milvusサーバーが既に起動していることを確認する。
from pymilvus import connections, utility # ミルバスサーバが起動していることを確認する。 from pymilvus import Collection, CollectionSchema, FieldSchema, DataType
Milvusサーバに接続する
connections.connect(alias='default', host='localhost', port='19530')
コレクション名
コレクション名 = 'hnm_fashion_images'
埋め込みサイズ
emb_dim = 512
コレクションが存在するかチェックし、存在すれば削除する
if utility.has_collection(collection_name):
print(utility.list_collections())
utility.drop_collection(collection_name)
オプションとして、`collection_name`で指定されたコレクションが既にMilvusサーバに存在するかどうかをチェックすることができます。この例では、コレクションがすでに利用可能であれば削除します。しかし、本番サーバではこのようなことはせず、以下のコレクション作成コードをスキップすることになるでしょう。
Milvusコレクションは従来のデータベースのテーブルに似ています。データを格納するコレクションを作成するには、まずコレクションの`スキーマ`を指定する必要があります。この例では、コレクションに2つのフィールドだけを保持します。主キー `article_id` は `INT64` データ型で、`img_embedding` は `emb_dim` 次元の埋め込みデータを含む `FLOAT_VECTOR` フィールドである。
python
# コレクションのスキーマを作成する。
article_id = FieldSchema(name='article_id', dtype=DataType.INT64, is_primary=True)
img_embedding = FieldSchema(name='img_embedding', dtype=DataType.FLOAT_VECTOR, dim=emb_dim)
# コレクションスキーマの設定
fields = [article_id, img_embedding] # コレクションスキーマの設定
schema = CollectionSchema(fields=fields, description='H&M Fashion Products')
# スキーマを使ってコレクションを作成する
コレクション = コレクション(name=コレクション名, schema=schema, using='default', shards_num=10)
コレクションが作成されたら、ベクターデータとスカラーデータをアップロードする準備ができました。
エンベッディングをバッチで生成し、Milvusコレクションにアップロードする。
前述の通り、OpenAIのCLIP (Contrastive Language-Image Pre-Training)を使って、Text-to-Imageマルチモーダル検索のための埋め込みデータを生成します。まず、必要なライブラリをインポートし、事前に学習されたCLIPモデルをインスタンス化しましょう。
python from sentence_transformers import SentenceTransformer model = SentenceTransformer('clip-ViT-B-32')
次に、データフレーム内の画像パスを512のバッチでループしてみましょう。まず、各バッチの `article_id` を主キーとする変数 `data` を作成します。次に、画像をパスから開き、上で読み込んだ事前学習済みモデルを使用して埋め込みます。埋め込みは[Milvus documentation](https://milvus.io/docs/v2.0.0/metric.md#Inner-product-IP)で指定されているIP距離を使用するために正規化する必要があります。最後に、正規化された埋め込みデータを同じ変数 `data` に追加し、Milvusコレクションに挿入する。
``python
# 各繰り返しで画像のバッチ埋め込みを生成する。
バッチサイズ = 512
clip_embeddings = [].
for idx in tqdm(range(0, len(image_df), batch_size)):
subset_df = image_df.iloc[idx:idx+batch_size].
# 主キー
data = [subset_df['article_id'].values.tolist()] # 主キー
# 埋め込み
## `batch_size` 個の画像を読み込む
images = [
Image.open(img_path).convert('RGB')
for img_path in subset_df['img_path'].
]
## 読み込んだ画像に対してCLIPエンベッディングを生成する
raw_embeddings = model.encode(images)
## IP 距離を使用するためにエンベッディングを正規化する
## https://milvus.io/docs/v2.0.0/metric.md#Inner-product-IP
norm_embeddings = normalize(raw_embeddings, axis=1).tolist()
## 埋め込みを追加する
data.append(norm_embeddings)
clip_embeddings += norm_embeddings
# データをmilvusに挿入する
collection.insert(データ)
オプションで、埋め込み生成処理は時間がかかるので、バックアップとして埋め込みデータをローカルのpickleファイルに保存している。
``python
埋め込みデータをファイルに保存する
image_df['clip_embeddings'] = clip_embeddings image_df.to_pickle('../data/image_embeddings_df.pkl')
### アップロードされたデータにANNインデックスを作成する。
すべての埋め込みデータをMilvusに挿入した後、検索を高速化するために`ANNインデックス`を作成する必要がある。この例では、FAISSの `IVF_PQ` インデックスタイプを使用しています。これは、膨大なデータサイズに対して最もスケーラブルなANNインデックスの一つです。IVF_PQインデックスとそのパラメータについての詳細は[Milvus documentation](https://milvus.io/docs/v2.0.x/index.md#IVF_PQ)を参照されたい。
python
# コレクションにANNインデックスを追加する
index_params = {
"metric_type": "IP"、
「index_type": "IVF_PQ"、
"params":{ "nlist":1024, "m":8}.
}
コレクション.create_index(field_name='img_embedding', index_params=index_params)
クエリを実行する!
最後に、Milvusコレクションのデータをクエリする準備ができました。まず、クエリを実行するためにコレクションをメモリにロードする必要があります。
``python
コレクションをメモリにロードする
コレクション = コレクション(コレクション名) collection.load()
次に、`query_text`を受け取り、CLIPエンベッディングに変換し、IP距離を使用できるように正規化する簡単なヘルパー関数を作成した。この関数は正規化されたエンベッディングを使ってMilvusコレクションをANN検索する。IVF_PQのドキュメント](https://milvus.io/docs/v2.0.x/index.md#IVF_PQ)に記述されている `search_params` を用いて、検索の質と速度を制御することができる。最後に、ヘルパー関数は検索結果とかかった時間をプロットする。
``python
def query_and_display(query_text, collection, product_mapping, num_results=10):
# クエリテキストを埋め込む
raw_embeddings = [model.encode(query_text)].
## コサイン類似度を使うために埋め込みを正規化する。
## https://milvus.io/docs/v2.0.0/metric.md#Inner-product-IP
query_emb = normalize(raw_embeddings, axis=1).tolist()
# 検索パラメータ
search_params = {"metric_type":"IP", "params":{"nprobe": 20}} # 検索パラメータ
# テキストから画像へのMilvus ANN検索
query_start = time.time()
results = collection.search(data=query_emb、
anns_field='img_embedding'、
param=search_params、
limit=num_results、
expr=None)
query_end = time.time()
# 検索結果を商品に変換する
result_products = [product_mapping[item] for item in results[0].ids] # 検索結果を商品に変換する
result_similarities = results[0].distances
# 検索結果をプロットする
ncols = 5
nrows = -(-len(result_products)//ncols)
fig = plt.figure(figsize=(20,5*nrows))
plt.suptitle('検索結果')
for idx, product in enumerate(result_products):
plt.subplot(nrows, ncols, idx+1)
img = Image.open(product['img_path']).convert('RGB')
plt.imshow(img)
plt.title(f'商品名:{product["prod_name"]}nCosine Similarity:{result_similarities[idx]:.3f}')
plt.suptitle(f'Query Text:{query_text}.クエリは{(query_end-query_start):.3f}秒で返された')
plt.tight_layout()
Milvusコレクションに保存されている10万5千枚の画像に対してText-to-Image検索を実行するために、ヘルパー関数をたった1行のコードで使用できるようになりました。CLIPエンベッディングの能力をMilvus上で実証するために、以下の例ではfloral top
という用語を検索している。返されたトップ10の商品はすべて花柄のトップスで、私の検索クエリにかなり関連しています!この検索にかかった時間はわずか13msで、ほとんどのアプリケーションの典型的な使用要件内に収まっています!
パイソン
検索語 "floral top" にマッチする商品をクエリーする
query_and_display('floral top', collection, product_mapping, num_results=10)

これ以上クエリを実行する必要がない場合、コレクションを解放してマシンのメモリを解放することができます。コレクションをメモリから解放しても、データはディスクに保存され、必要なときに再びロードできるので、データが失われることはありません。
``python
# 不要になったらコレクションをメモリから解放する
collection.release()
Milvusサーバを停止してディスクから全てのデータを削除したい場合は、the stop-milvus documentationの指示に従えばよい。注意してください!この操作は不可逆であり、Milvusクラスタ内のすべてのデータを削除します。
結論
この投稿では、CLIPエンベッディングとMilvusを使った超スケーラブルなText-to-Image eコマース商品検索サービスを簡単なステップで実装しました。このアプローチは、数億から数十億のベクトルまでスケーラブルです。サンプルテキストクエリを使用して検索をテストしたところ、わずか13msでトップ10の結果が返されました!Milvusは非常にスケーラブルで高速なベクトル検索データベースとして高い評価を得ています!
Milvusの逆画像検索への応用については、Milvus demosおよびMilvus bootcampをご覧ください。
読み続けて

ベクトル類似度検索でワードローブを持続可能にする方法
ベクターデータベースを使用して、類似した衣服を検索できるインテリジェントな服の推薦アプリを構築する方法を学ぶ。

ニューラルネットワークの埋め込みを理解する
この記事では、エンベッディング/エンベッディングベクターについて、最新のMLアルゴリズムやパイプラインでどのように使われているのか、もう少し掘り下げて解説します。

ベクターデータベースでIoT分析とデバイスデータに革命を起こす
ベクターデータベースは、IoTデバイスの特徴である高次元データの管理に特化したもので、従来のデータ管理システムを挫折させる「量」「速度」「多様性」「真実性」という固有の課題に対処する最前線に立つ。この特化したデータ処理は、効率性、正確性、拡張性を特徴とするIoTデータ活用の新時代の到来を告げる技術的改善であり、パラダイムシフトである。