「Chromaを使用してマルチモーダル検索アプリを作成する方法」
「クローマを活用したマルチモーダル検索アプリの作り方」
はじめに
複雑な脳が世界をどのように処理しているのか、あなたは考えたことがありますか? 脳の内部の仕組みは依然として謎ですが、私たちはそれを多目的なニューラルネットワークにたとえることができます。 電気化学的な信号のおかげで、それは様々なデータ型を処理します-音、映像、匂い、味、触覚。 AIが進化するにつれて、マルチモーダルモデルが登場し、検索能力が革新されています。 このイノベーションにより、検索の正確性と関連性が向上し、新たな可能性が開かれています。 マルチモーダル検索の魅力的な世界を発見しましょう。
学習目標
- 「AIにおけるマルチモーダリティ」という用語を理解する。
- OpenAIのイメージテキストモデルCLIPについての洞察を得る。
- ベクトルデータベースとベクトルインデックスの概要を理解する。
- CLIPとChromaベクトルデータベースを使用して、Gradioインターフェースを使用した食品推薦システムを構築する。
- マルチモーダル検索の他の現実世界での使用例を探索する。
この記事はData Science Blogathonの一部として公開されました。
AIにおけるマルチモーダリティとは何ですか?
Googleで検索すると、マルチモードはプロセスに複数のモードや方法を関与させることを指すと分かります。 人工知能では、マルチモーダルモデルは異なるデータタイプを処理し理解することができるニューラルネットワークです。 たとえば、GPT-4やバードなどです。 これらは、テキストや画像を理解できるLLMです。 他の例としては、ビジュアルとセンサーデータを組み合わせて周囲の状況を理解するテスラの自動運転車、またはテキストの説明から画像を生成できるMidjourneyやDalleがあります。
コントラスト言語-画像事前トレーニング(CLIP)
CLIPは、OpenAIが大量の画像テキストペアのデータセットでトレーニングしたオープンソースのマルチモーダルニューラルネットワークです。 これにより、CLIPは画像内の視覚的な概念をテキストの説明と関連付けることを学びます。 CLIPモデルは、特定のトレーニングを行わずに、人間の言語で指示されて幅広い画像データを分類することができます。
- 「迅速エンジニアリングのための普遍的な道筋:コンテクストの足場フレームワーク(CSF)」
- 「自分自身でタスクを行う方法を知っている場合に限り、LLMsを使用してください」
- 「Pydantic V2の強化されたデータ検証機能を探索する」
CLIPのゼロショット能力はGPT 3と比較可能です。 したがって、CLIPは特定のカテゴリに対してトレーニングを受けることなく、画像を任意のカテゴリに分類するために使用することができます。 たとえば、犬と猫の画像を分類するには、画像のロジットスコアを「犬の画像」または「猫の画像」というテキストの説明と比較するだけです。猫または犬の写真は、それぞれのテキストの説明とより高いロジットスコアを持つ可能性が高いです。
これはゼロショット分類として知られており、CLIPは犬や猫の画像のデータセットでトレーニングされる必要がありません。 CLIPの動作方法のビジュアルプレゼンテーションは次のとおりです。
CLIPは画像のためにVision Transformer(ViT)、テキストの特徴のためにテキストモデルを使用します。 ベクトルエンコーディングは、同一の次元を持つ共有ベクトル空間に射影されます。 テキストのスニペットと画像の間の類似性を予測するために、その2つの内積が類似スコアとして使用されます。 言い換えれば、CLIPはそれに最適化されていなくても、画像を任意のカテゴリに分類することができます。 この記事では、CLIPをプログラム的に実装します。
ベクトルデータベースはなぜ必要ですか?
機械学習アルゴリズムはデータを生の形式では理解できません。 したがって、それを機能させるためには、データを数値形式に変換する必要があります。 ベクトルまたは埋め込みは、テキスト、画像、オーディオ、ビデオなどのさまざまなデータタイプの数値表現です。 ただし、従来のデータベースは高次元のベクトルデータのクエリを完全に処理できません。 数百万のベクトル埋め込みを使用するアプリケーションを構築するには、それらを保存、検索、クエリすることができるデータベースが必要です。 これは従来のデータベースでは不可能です。 このために、ベクトルデータベースが必要です。 ベクトルデータベースは埋め込みを保存し、クエリするために特化されたデータベースです。
以下の図は、ベクトルデータベースのシンプルなワークフローを示しています。
私たちは、データの潜在的な意味を捉えることができる専門の埋め込みモデルが必要です。モデルはデータの種類によって異なります。画像データにはResnetやVisual Transformersなどの画像モデルを使用します。テキストにはAdaやSentenceTransformersなどのテキストモデルが使用されます。クロスモーダルインタラクションには、Tortoise(テキストから音声への変換)やCLIP(テキストから画像への変換)などのマルチモーダルモデルが使用されます。これらのモデルは、入力データの埋め込みを取得するために使用されます。ベクトルデータベースには通常、埋め込みモデルのカスタム実装がありますが、埋め込みを取得しベクトルストアに保存するためにモデルを定義することもできます。
インデックス化
埋め込みは通常、高次元であり、高次元ベクトルのクエリは時間と計算量がかかることがよくあります。そのため、ベクトルデータベースでは効率的なクエリを提供するためにさまざまなインデックス化手法が使用されます。インデックス化とは、近傍ベクトルの効率的なクエリを提供する方法で高次元ベクトルを組織化することを指します。
いくつかの人気のあるインデックス化アルゴリズムは、HNSW(Hierarchical Navigable Small World)、Product Quantizing、Inverted File System、Scalar Quantizationなどです。これらの中で、HNSWは異なるベクトルデータベースで最も人気があり広く使用されるアルゴリズムです。
このアプリケーションでは、クロマベクトルデータベースを使用します。クロマはオープンソースのベクトルデータベースです。ベクトルと関連するメタデータを格納してクエリを行うためのクライアントを迅速に設定できます。Weaviate、Qdrant、Milvusなど、他のベクトルストアも使用できます。
Gradioとは何ですか?
GradioはPythonで書かれたオープンソースのツールで、機械学習モデルを共有するためのウェブインターフェースを素早く構築することを目指しています。Pythonを使用してデモ用のウェブインターフェースを簡単に設定できます。バックエンドモデルを示すためのデセントなプロトタイプを作成する柔軟性を提供します。
詳しくは、この記事を参照してください。
アプリの構築
このセクションでは、Gradio、クロマ、およびCLIPを使用してシンプルなレストランの料理レコメンダーアプリを作成するコードを説明します。クロマはまだマルチモーダルモデルのアウトオブボックスサポートを持っていません。そのため、これは回避策となります。
プロジェクトでCLIPを使用するには、OpenAIのCLIP実装かHuggingfaceのCLIPの実装のいずれかを使用することができます。このプロジェクトでは、OpenAIのCLIPを使用します。以下の依存関係がインストールされている仮想環境を持っていることを確認してください。
cliptorchchromadbgradio
これが私たちのディレクトリ構造です。
├── app.py├── clip_chroma├── clip_embeddings.py├── __init__.py├── load_data.py
CLIPの埋め込み
最初に行うことは、画像とテキストの埋め込みを抽出するためのクラスを作ることです。CLIPには、テキストと画像を処理するための2つのパートがあることを知っています。異なるモダリティをエンコードするために、それぞれのモデルを使用します。
import clip import torchfrom numpy import ndarray from typing import List from PIL import Image class ClipEmbeddingsfunction: def __init__(self, model_name: str = "ViT-B/32", device: str = "cpu"): self.device = device # モデルの実行のために指定されたデバイスを保持する self.model, self.preprocess = clip.load(model_name, self.device) def __call__(self, docs: List[str]) -> List[ndarray]: # 画像ファイルパスのリスト(docs)を入力とするメソッドを定義 list_of_embeddings = [] # 画像の埋め込みを格納するための空のリストを作成 for image_path in docs: image = Image.open(image_path) # 提供されたパスから画像を開いて読み込む image = image.resize((224, 224)) # 画像を前処理し、指定されたデバイスに移動 image_input = self.preprocess(image).unsqueeze(0).to(self.device) with torch.no_grad(): # CLIPモデルを使用して画像の埋め込みを計算し、NumPy配列に変換する embeddings = self.model.encode_image(image_input).cpu().detach().numpy() list_of_embeddings.append(list(embeddings[0])) return list_of_embeddings def get_text_embeddings(self, text: str) -> List[ndarray]: # テキスト文字列を入力とするメソッドを定義 text_token = clip.tokenize(text) # 入力テキストをトークン化する with torch.no_grad(): # CLIPモデルを使用してテキストの埋め込みを計算し、NumPy配列に変換する text_embeddings = self.model.encode_text(text_token).cpu().detach().numpy() return list(text_embeddings[0])
上記のコードでは、テキストと画像の埋め込みを抽出するためのクラスを定義しています。このクラスは、モデル名とデバイスを入力として受け取ります。デバイスがCudaをサポートしている場合は、デバイスを渡すことで有効にすることができます。CLIPは、次のようないくつかのモデルをサポートしています
clip.available_models()['RN50', 'RN101', 'RN50x4', 'RN50x16', 'RN50x64', 'ViT-B/32', 'ViT-B/16', 'ViT-L/14', 'ViT-L/14@336px']
モデル名はデフォルトで「ViT-B/32」として設定されています。他のモデルを指定することもできます。
__call__メソッドは、画像パスのリストを受け取り、numpy配列のリストを返します。get_text_embeddingsメソッドは、文字列の入力を受け取り、埋め込みのリストを返します。
埋め込みの読み込み
まず、ベクトルデータベースを準備する必要があります。そのために、料理のいくつかの画像を収集してコレクションに追加しました。画像パスはドキュメントとして、画像の説明をメタデータとして保存します。
まずは、Chromaコレクションを作成します。
import osfrom chromadb import Client, Settingsfrom clip_embeddings import ClipEmbeddingsfunctionfrom typing import Listef = ClipEmbeddingsfunction()client = Client(settings = Settings(is_persistent=True, persist_directory="./clip_chroma"))coll = client.get_or_create_collection(name = "clip", embedding_function = ef)
先に定義した埋め込み関数をインポートし、コレクションのデフォルトの埋め込み関数として渡しました。
さて、データをデータベースにロードします。
coll.add(ids=[str(i) for i in range(len(img_list))], documents = img_list, #画像へのパス metadatas = menu_description,# 料理の説明 )
これで準備完了です。最後のパートを構築する準備ができました。
Gradioアプリケーション
まず、app.pyというファイルを作成し、以下の依存関係をインポートし、埋め込み関数を初期化します。
import gradio as grfrom chromadb import Client, Settingsfrom clip_embeddings import ClipEmbeddingsfunctionclient = Client(Settings(is_persistent=True, persist_directory="./clip_chroma"))ef = ClipEmbeddingsfunction()
フロントエンドとして、テキストまたは画像の検索クエリを受けるシンプルなインターフェースを構築するためにこれを使用します。
with gr.Blocks() as demo: with gr.Row(): with gr.Column(): query = gr.Textbox(placeholder = "クエリを入力してください") gr.HTML("または") photo = gr.Image() button = gr.UploadButton(label = "ファイルをアップロード", file_types=["image"]) with gr.Column(): gallery = gr.Gallery().style( object_fit='contain', height='auto', preview=True )
次に、gradioアプリケーションのトリガーイベントを定義します。
query.submit( fn = retrieve_image_from_query, inputs=[query], outputs= ) button.upload( fn = show_img, inputs=[button], outputs = [photo]).\ then( fn = retrieve_image_from_image, inputs=[button], outputs= )
上記のコードでは、トリガーイベントがあります。retrieve_image_from_query関数でテキストクエリを処理します。まず、写真オブジェクトに画像をレンダリングし、次にretrieve_image_from_image()を呼び出して、出力をギャラリーオブジェクトに表示します。
gradioコマンドでapp.pyファイルを実行し、ターミナルに表示されたローカルアドレスにアクセスしてください。
次に、実際の関数を定義します。
def retrieve_image_from_image(image): # 指定した埋め込み関数(ef)を使用して名前が"clip"という名前のコレクションを取得 coll = client.get_collection(name="clip", embedding_function=ef) # 画像ファイルの名前を抽出 image = image.name # 画像ファイル名をクエリテキストとしてコレクションにクエリを実行 result = coll.query( query_texts=image, # クエリテキストとして画像ファイル名を使用 include=["documents", "metadatas"], # 結果にドキュメントとメタデータの両方を含める n_results=4 # 取得する結果の数を指定 ) # 取得したドキュメントとそのメタデータを取得 docs = result['documents'][0] descs = result["metadatas"][0] # ドキュメントと対応するメタデータのペアを格納するリストを作成 list_of_docs = [] # 取得したドキュメントとメタデータを反復処理する for doc, desc in zip(docs, descs): # リストに、ドキュメントとそのメタデータを含むタプルを追加 list_of_docs.append((doc, list(desc.values())[0])) # ドキュメント-メタデータのリストを返す return list_of_docs
テキストクエリを処理するための別の機能もあります。
def retrieve_image_from_query(query: str): # 指定された埋め込み関数(ef)を使用して "clip" という名前のコレクションを取得する coll = client.get_collection(name="clip", embedding_function=ef) # 入力クエリのテキスト埋め込みを取得する emb = ef.get_text_embeddings(text=query) # テキスト埋め込みを浮動小数点数値に変換する emb = [float(i) for i in emb] # テキスト埋め込みを使用してコレクションをクエリする result = coll.query( query_embeddings=emb, # クエリとしてテキスト埋め込みを使用 include=["documents", "metadatas"], # 結果にドキュメントとメタデータの両方を含める n_results=4 # 取得する結果の数を指定 ) # 取得したドキュメントとそのメタデータを取得する docs = result['documents'][0] descs = result["metadatas"][0] # ドキュメントと対応するメタデータのペアを保存するリストを作成する list_of_docs = [] # 取得したドキュメントとメタデータを反復処理する for doc, desc in zip(docs, descs): # リストに、ドキュメントとそのメタデータを含むタプルを追加する list_of_docs.append((doc, list(desc.values())[0])) # ドキュメントとメタデータのペアのリストを返す return list_of_docs
コード内で直接テキストを渡す代わりに、埋め込みを抽出してそれを Choma のクエリメソッドに渡しました。
では、app.py の完全なコードです。
# 必要なライブラリをインポートするimport gradio as grfrom chromadb import Client, Settingsfrom clip_embeddings import ClipEmbeddingsfunction# 永続ストレージを使用して chromadb クライアントを初期化するclient = Client(Settings(is_persistent=True, persist_directory="./clip_chroma"))# ClipEmbeddingsfunction を初期化するef = ClipEmbeddingsfunction()# テキストクエリから画像を取得する関数def retrieve_image_from_query(query: str): # 指定された埋め込み関数を使用して "clip" という名前のコレクションを取得する coll = client.get_collection(name="clip", embedding_function=ef) # 入力クエリのテキスト埋め込みを取得する emb = ef.get_text_embeddings(text=query) emb = [float(i) for i in emb] # 類似のドキュメントをクエリする result = coll.query( query_embeddings=emb, include=["documents", "metadatas"], n_results=4 ) # ドキュメントとそのメタデータを取得する docs = result['documents'][0] descs = result["metadatas"][0] list_of_docs = [] # ドキュメントと説明をリストに組み合わせる for doc, desc in zip(docs, descs): list_of_docs.append((doc, list(desc.values())[0])) return list_of_docs# アップロードされた画像から画像を取得する関数def retrieve_image_from_image(image): # 指定された埋め込み関数を使用して "clip" という名前のコレクションを取得する coll = client.get_collection(name="clip", embedding_function=ef) # アップロードされた画像のファイル名を取得する image = image.name # 画像ファイル名でコレクションをクエリする result = coll.query( query_texts=image, include=["documents", "metadatas"], n_results=4 ) # ドキュメントとそのメタデータを取得する docs = result['documents'][0] descs = result["metadatas"][0] list_of_docs = [] # ドキュメントと説明をリストに組み合わせる for doc, desc in zip(docs, descs): list_of_docs.append((doc, list(desc.values())[0])) return list_of_docs# 画像を表示する関数def show_img(image): return image.name# gr.Blocks() を使用してインターフェースを作成するwith gr.Blocks() as demo: # gr.Row() 内にグラフィカルな要素を作成する with gr.Row(): with gr.Column(): # クエリのためのテキスト入力 query = gr.Textbox(placeholder="クエリを入力してください") gr.HTML("または") # ファイルアップロードを介した画像入力 photo = gr.Image() button = gr.UploadButton(label="ファイルをアップロード", file_types=["image"]) with gr.Column(): # 画像のギャラリーを表示する gallery = gr.Gallery().style( object_fit='contain', height='auto', preview=True ) # クエリの送信に対する入力と出力を定義する query.submit( fn=retrieve_image_from_query, inputs=[query], outputs= ) # 画像のアップロードに対する入力と出力を定義する button.upload( fn=show_img, inputs=[button], outputs=[photo]).\ then( fn=retrieve_image_from_image, inputs=[button], outputs= )# スクリプトがメインプログラムとして実行された場合、Gradio インターフェースを起動するif __name__ == "__main__": demo.launch()
アプリを起動するには、ターミナルでgadio app.pyを実行し、ローカルアドレスを訪問してください。
GitHub リポジトリ: https://github.com/sunilkumardash9/multi-modal-search-app
実生活でのユースケース
マルチモーダル検索は、さまざまな業界で多くの用途があります。
- 電子商取引:マルチモーダル検索は、顧客のショッピング体験を向上させることができます。たとえば、実際の店舗で商品の写真を撮影し、それと似た商品をオンラインで検索することができます。
- 医療:これは疾患の診断や治療を助けることができます。医師は画像を使用して医療データベースから臨床研究データを見つけることができます。
- 教育:マルチモーダル検索を可能にする教育アプリは、学生や教授が関連するドキュメントをより迅速に見つけるのに役立ちます。画像に基づいてテキストを検索したり、その逆を行ったりすることで、多くの時間を節約できます。
- カスタマーサービス:マルチモーダル検索は、ナレッジベースから顧客の問い合わせに関連する回答を効率的に検索するのに役立ちます。これらの問い合わせには、商品の画像や動画が含まれる場合もあります。
結論
マルチモーダル検索は将来的にはゲームチェンジングとなるでしょう。複数のモダリティでの相互作用が可能になることで、新たな成長の道が開けます。したがって、この記事では、クロマベクトルデータベースとマルチモーダルCLIPモデルを使用して基本的な検索アプリを構築する方法について紹介しました。クロマデータベースはマルチモーダルモデルのテンプレートサポートを提供していないため、カスタムのCLIP埋め込みクラスを作成して、画像から埋め込みを取得し、様々な部分を組み合わせて食品検索アプリを構築しました。
要点
- AIでは、テキスト、画像、音声、ビデオなどの複数の通信モードとの相互作用ができることがマルチモダリティと言われます。
- CLIPは、最新のゼロショット分類能力を持つ数千の画像テキストサンプルでトレーニングされた画像テキストモデルです。
- ベクトルデータベースは、高次元ベクトルの格納、検索、クエリに特化した目的で作られたデータベースです。
- ベクトルストアを活性化させるエンジンはANNアルゴリズムです。HNSWは最も人気のある効率的なグラフベースのANNアルゴリズムの1つです。
よくある質問
この記事中に表示されるメディアはAnalytics Vidhyaの所有ではなく、著者の裁量で使用されています。
We will continue to update VoAGI; if you have any questions or suggestions, please contact us!
Was this article helpful?
93 out of 132 found this helpful
Related articles