paint-brush
画像とテキストの説明を使って何かを検索したいですか? マルチモーダル RAG をお試しください@codingjaguar
新しい歴史

画像とテキストの説明を使って何かを検索したいですか? マルチモーダル RAG をお試しください

Jiang Chen16m2024/11/27
Read on Terminal Reader

長すぎる; 読むには

Milvus を使用してマルチモーダル RAG システムを構築する方法と、AI システムのさまざまな可能性を切り開く方法についての詳細なガイド。
featured image - 画像とテキストの説明を使って何かを検索したいですか? マルチモーダル RAG をお試しください
Jiang Chen HackerNoon profile picture
0-item
1-item

この記事では、Milvus を使用してマルチモーダル RAG システムを構築する方法と、AI システムのさまざまな可能性を切り開く方法について詳しく説明します。


単一のデータ形式に制限されるだけではもはや十分ではありません。企業が重要な決定を下すために情報に大きく依存するようになるにつれ、異なる形式のデータを比較する能力が必要になります。幸いなことに、単一のデータ タイプに制限される従来の AI システムは、複雑な情報を理解して処理できるマルチモーダル システムに取って代わられました。


マルチモーダル検索およびマルチモーダル検索拡張生成(RAG) システムは、最近この分野で大きな進歩を見せています。これらのシステムは、テキスト、画像、音声など、複数の種類のデータを処理して、コンテキストに応じた応答を提供します。


このブログ記事では、開発者が Milvus を使用して独自のマルチモーダル RAG システムを構築する方法について説明します。また、テキストと画像データを処理し、特に類似性検索を実行し、言語モデルを活用して出力を改良できるシステムの構築についても説明します。それでは、始めましょう。

Milvus とは何ですか?

ベクター データベースは、ベクター埋め込みを保存、インデックス付け、および取得するために使用される特殊なタイプのデータベースです。ベクター埋め込みは、データの同等性だけでなく意味の類似性を比較できるようにするデータの数学的表現です。ミルバスは、大規模に構築されたオープンソースの高性能ベクター データベースです。Apache 2.0 ライセンスで 30,000 を超えるスターが付いた GitHub で入手できます。


Milvus は、大規模なベクター データを管理およびクエリするための柔軟なソリューションを開発者が提供できるように支援します。その効率性により、Milvus は、検索拡張生成 (RAG)、マルチモーダル検索、推奨エンジン、異常検出などのディープラーニング モデルを使用してアプリケーションを構築する開発者にとって理想的な選択肢となります。


Milvus は、開発者のニーズに合わせて複数の展開オプションを提供します。ミルバス ライトは、Python アプリケーション内で実行される軽量バージョンであり、ローカル環境内でのアプリケーションのプロトタイプ作成に最適です。Milvus Standalone と Milvus Distributed は、スケーラブルで本番環境に対応したオプションです。

マルチモーダル RAG: テキストを超えた拡張

システムを構築する前に、従来のテキストベースの RAG と、そのマルチモーダル RAG への進化を理解することが重要です。


検索拡張生成(RAG) は、外部ソースからコンテキスト情報を取得し、大規模言語モデル (LLM) からより正確な出力を生成する方法です。従来の RAG は LLM 出力を改善するための非常に効果的な戦略ですが、テキスト データに限定されています。多くの実際のアプリケーションでは、データはテキストの範囲を超えており、画像、グラフ、その他のモダリティを組み込むことで重要なコンテキストが提供されます。


マルチモーダル RAG は、さまざまなデータ タイプの使用を可能にし、LLM に優れたコンテキストを提供することで、上記の制限に対処します。


簡単に言えば、マルチモーダル RAG システムでは、検索コンポーネントがさまざまなデータ モダリティにわたって関連情報を検索し、生成コンポーネントが取得された情報に基づいてより正確な結果を生成します。

ベクトル埋め込みと類似性検索を理解する

ベクトル埋め込み類似性検索は、マルチモーダル RAG の 2 つの基本概念です。この両方を理解しましょう。

ベクトル埋め込み

前述したように、ベクトル埋め込みはデータの数学的/数値的表現です。マシンはこの表現を使用して、テキスト、画像、音声などのさまざまなデータ タイプの意味を理解します。


自然言語処理 (NLP) を使用すると、ドキュメントのチャンクがベクトルに変換され、意味的に類似した単語がベクトル空間内の近くのポイントにマッピングされます。画像についても同様で、埋め込みによって意味的特徴が表されます。これにより、色、テクスチャ、オブジェクトの形状などの指標を数値形式で理解できます。


ベクトル埋め込みを使用する主な目的は、さまざまなデータ間の関係性と類似性を維持することです。

類似検索

類似性検索は、特定のデータセット内のデータを検索して特定するために使用されます。ベクトル埋め込みのコンテキストでは、類似性検索は、特定のデータセット内でクエリ ベクトルに最も近いベクトルを検索します。


以下は、ベクトル間の類似性を測定するために一般的に使用されるいくつかの方法です。

  1. ユークリッド距離: ベクトル空間内の 2 点間の直線距離を測定します。
  2. コサイン類似度: 2 つのベクトル間の角度のコサインを測定します (大きさではなく方向に重点を置きます)。
  3. ドット積: 対応する要素を単純に掛け合わせて合計したもの。


類似度の測定方法の選択は通常、アプリケーション固有のデータと開発者が問題にどのようにアプローチするかによって異なります。


大規模なデータセットで類似性検索を実行する場合、必要な計算能力とリソースは非常に高くなります。ここで、近似最近傍 (ANN) アルゴリズムが役立ちます。ANN アルゴリズムは、わずかな割合または量の精度を犠牲にして、大幅な速度向上を実現するために使用されます。そのため、大規模なアプリケーションには適した選択肢となります。


Milvus は、HNSW や DiskANN などの高度な ANN アルゴリズムも使用して、大規模なベクター埋め込みデータセットに対して効率的な類似性検索を実行し、開発者が関連するデータ ポイントをすばやく見つけられるようにします。さらに、Milvus は HSNW、IVF、CAGRA などの他のインデックス アルゴリズムもサポートしているため、より効率的なベクター検索ソリューションとなっています。


Milvus によるマルチモーダル RAG の構築

概念を学んだので、次は Milvus を使用してマルチモーダル RAG システムを構築します。この例では、ベクトルの保存と検索に Milvus Lite (実験やプロトタイピングに最適な Milvus の軽量バージョン)、正確な画像処理と埋め込みに BGE、高度な結果の再ランク付けに GPT-4o を使用します。

前提条件

まず、データを保存するための Milvus インスタンスが必要です。pip を使用して Milvus Lite をセットアップしたり、Docker を使用してローカル インスタンスを実行したり、Zilliz Cloud を通じて無料でホストされる Milvus アカウントにサインアップしたりできます。


次に、RAGパイプラインにはLLMが必要です。オープンAI API キーを取得します。このコードを動作させるには無料レベルで十分です。


次に、新しいディレクトリとPythonを作成します仮想環境(または、Python を管理するために使用する任意の手順を実行します)。


このチュートリアルでは、ピミルバスMilvus の公式 Python SDK であるライブラリと、いくつかの一般的なツールが含まれています。

Milvus Liteをセットアップする

pip install -U pymilvus

依存関係をインストールする

pip install --upgrade pymilvus openai datasets opencv-python timm einops ftfy peft tqdm git clone https://github.com/FlagOpen/FlagEmbedding.git pip install -e FlagEmbedding

データをダウンロード

次のコマンドは、サンプル データをダウンロードし、ローカル フォルダー「./images_folder」に抽出します。このフォルダーには次の内容が含まれます。


  • 画像:サブセットAmazon レビュー 2023 「家電」、「携帯電話とアクセサリ」、「電子機器」のカテゴリから約 900 枚の画像が含まれています。
  • クエリ画像の例: leopard.jpg


 wget https://github.com/milvus-io/bootcamp/releases/download/data/amazon_reviews_2023_subset.tar.gz tar -xzf amazon_reviews_2023_subset.tar.gz

埋め込みモデルを読み込む

画像とテキストの両方の埋め込みを生成するために、視覚化された BGE モデル「bge-visualized-base-en-v1.5」を使用します。


次に、HuggingFace からウェイトをダウンロードします。


 wget https://huggingface.co/BAAI/bge-visualized/resolve/main/Visualized_base_en_v1.5.pth


それでは、エンコーダを構築してみましょう。

 import torch from visual_bge.modeling import Visualized_BGE class Encoder:    def __init__(self, model_name: str, model_path: str):        self.model = Visualized_BGE(model_name_bge=model_name, model_weight=model_path)        self.model.eval()    def encode_query(self, image_path: str, text: str) -> list[float]:        with torch.no_grad():            query_emb = self.model.encode(image=image_path, text=text)        return query_emb.tolist()[0]    def encode_image(self, image_path: str) -> list[float]:        with torch.no_grad():            query_emb = self.model.encode(image=image_path)        return query_emb.tolist()[0] model_name = "BAAI/bge-base-en-v1.5" model_path = "./Visualized_base_en_v1.5.pth" # Change to your own value if using a different model path encoder = Encoder(model_name, model_path)

埋め込みを生成し、Milvus にデータをロードする

このセクションでは、対応する埋め込みとともにサンプル画像をデータベースに読み込む方法について説明します。


埋め込みを生成する


まず、データセット内のすべての画像の埋め込みを作成する必要があります。


データ ディレクトリからすべての画像を読み込み、埋め込みに変換します。


 import os from tqdm import tqdm from glob import glob data_dir = (    "./images_folder" # Change to your own value if using a different data directory ) image_list = glob(    os.path.join(data_dir, "images", "*.jpg") ) # We will only use images ending with ".jpg" image_dict = {} for image_path in tqdm(image_list, desc="Generating image embeddings: "):    try:        image_dict[image_path] = encoder.encode_image(image_path)    except Exception as e:        print(f"Failed to generate embedding for {image_path}. Skipped.")        continue print("Number of encoded images:", len(image_dict))

マルチモーダル検索を実行し、結果を再ランク付けする

このセクションでは、まずマルチモーダル クエリを使用して関連する画像を検索し、次に LLM サービスを使用して取得した結果を再ランク付けし、説明付きの最適な画像を見つけます。


マルチモーダル検索を実行する


これで、画像とテキストの指示で構成されたクエリを使用して、高度なマルチモーダル検索を実行する準備が整いました。


 query_image = os.path.join(    data_dir, "leopard.jpg" ) # Change to your own query image path query_text = "phone case with this image theme" query_vec = encoder.encode_query(image_path=query_image, text=query_text) search_results = milvus_client.search(    collection_name=collection_name,    data=[query_vec],    output_fields=["image_path"],    limit=9, # Max number of search results to return    search_params={"metric_type": "COSINE", "params": {}}, # Search parameters )[0] retrieved_images = [hit.get("entity").get("image_path") for hit in search_results] print(retrieved_images)


結果は以下の通りです。


 ['./images_folder/images/518Gj1WQ-RL._AC_.jpg', './images_folder/images/41n00AOfWhL._AC_.jpg'


GPT-4oで結果を再ランク付けする


ここで、GPT-4o を使用して取得した画像をランク付けし、最も一致する結果を見つけます。LLM は、なぜそのようにランク付けされるのかについても説明します。


1. パノラマビューを作成します。


 import numpy as np import cv2 img_height = 300 img_width = 300 row_count = 3 def create_panoramic_view(query_image_path: str, retrieved_images: list) -> np.ndarray:    """    creates a 5x5 panoramic view image from a list of images    args:        images: list of images to be combined    returns:        np.ndarray: the panoramic view image    """    panoramic_width = img_width * row_count    panoramic_height = img_height * row_count    panoramic_image = np.full(        (panoramic_height, panoramic_width, 3), 255, dtype=np.uint8    )    # create and resize the query image with a blue border    query_image_null = np.full((panoramic_height, img_width, 3), 255, dtype=np.uint8)    query_image = Image.open(query_image_path).convert("RGB")    query_array = np.array(query_image)[:, :, ::-1]    resized_image = cv2.resize(query_array, (img_width, img_height))    border_size = 10    blue = (255, 0, 0) # blue color in BGR    bordered_query_image = cv2.copyMakeBorder(        resized_image,        border_size,        border_size,        border_size,        border_size,        cv2.BORDER_CONSTANT,        value=blue,    )    query_image_null[img_height * 2 : img_height * 3, 0:img_width] = cv2.resize(        bordered_query_image, (img_width, img_height)    )    # add text "query" below the query image    text = "query"    font_scale = 1    font_thickness = 2    text_org = (10, img_height * 3 + 30)    cv2.putText(        query_image_null,        text,        text_org,        cv2.FONT_HERSHEY_SIMPLEX,        font_scale,        blue,        font_thickness,        cv2.LINE_AA,    )    # combine the rest of the images into the panoramic view    retrieved_imgs = [        np.array(Image.open(img).convert("RGB"))[:, :, ::-1] for img in retrieved_images    ]    for i, image in enumerate(retrieved_imgs):        image = cv2.resize(image, (img_width - 4, img_height - 4))        row = i // row_count        col = i % row_count        start_row = row * img_height        start_col = col * img_width        border_size = 2        bordered_image = cv2.copyMakeBorder(            image,            border_size,            border_size,            border_size,            border_size,            cv2.BORDER_CONSTANT,            value=(0, 0, 0),        )        panoramic_image[            start_row : start_row + img_height, start_col : start_col + img_width        ] = bordered_image        # add red index numbers to each image        text = str(i)        org = (start_col + 50, start_row + 30)        (font_width, font_height), baseline = cv2.getTextSize(            text, cv2.FONT_HERSHEY_SIMPLEX, 1, 2        )        top_left = (org[0] - 48, start_row + 2)        bottom_right = (org[0] - 48 + font_width + 5, org[1] + baseline + 5)        cv2.rectangle(            panoramic_image, top_left, bottom_right, (255, 255, 255), cv2.FILLED        )        cv2.putText(            panoramic_image,            text,            (start_col + 10, start_row + 30),            cv2.FONT_HERSHEY_SIMPLEX,            1,            (0, 0, 255),            2,            cv2.LINE_AA,        )    # combine the query image with the panoramic view    panoramic_image = np.hstack([query_image_null, panoramic_image])    return panoramic_image


2. クエリ画像と取得した画像をインデックス付きでパノラマビューに組み合わせます。


 from PIL import Image combined_image_path = os.path.join(data_dir, "combined_image.jpg") panoramic_image = create_panoramic_view(query_image, retrieved_images) cv2.imwrite(combined_image_path, panoramic_image) combined_image = Image.open(combined_image_path) show_combined_image = combined_image.resize((300, 300)) show_combined_image.show() 


マルチモーダル検索結果

3. 結果を再ランク付けし、説明を加える


我々は、すべての結合画像を適切なプロンプトとともにマルチモーダルLLMサービスに送信し、検索結果を説明とともにランク付けします。注:GPT-4oをLLMとして有効にするには、 OpenAI APIキーあらかじめ。


 import requests import base64 openai_api_key = "sk-***" # Change to your OpenAI API Key def generate_ranking_explanation(    combined_image_path: str, caption: str, infos: dict = None ) -> tuple[list[int], str]:    with open(combined_image_path, "rb") as image_file:        base64_image = base64.b64encode(image_file.read()).decode("utf-8")    information = (        "You are responsible for ranking results for a Composed Image Retrieval. "        "The user retrieves an image with an 'instruction' indicating their retrieval intent. "        "For example, if the user queries a red car with the instruction 'change this car to blue,' a similar type of car in blue would be ranked higher in the results. "        "Now you would receive instruction and query image with blue border. Every item has its red index number in its top left. Do not misunderstand it. "        f"User instruction: {caption} \n\n"    )    # add additional information for each image    if infos:        for i, info in enumerate(infos["product"]):            information += f"{i}. {info}\n"    information += (        "Provide a new ranked list of indices from most suitable to least suitable, followed by an explanation for the top 1 most suitable item only. "        "The format of the response has to be 'Ranked list: []' with the indices in brackets as integers, followed by 'Reasons:' plus the explanation why this most fit user's query intent."    )    headers = {        "Content-Type": "application/json",        "Authorization": f"Bearer {openai_api_key}",    }    payload = {        "model": "gpt-4o",        "messages": [            {                "role": "user",                "content": [                    {"type": "text", "text": information},                    {                        "type": "image_url",                        "image_url": {"url": f"data:image/jpeg;base64,{base64_image}"},                    },                ],            }        ],        "max_tokens": 300,    }    response = requests.post(        "https://api.openai.com/v1/chat/completions", headers=headers, json=payload    )    result = response.json()["choices"][0]["message"]["content"]    # parse the ranked indices from the response    start_idx = result.find("[")    end_idx = result.find("]")    ranked_indices_str = result[start_idx + 1 : end_idx].split(",")    ranked_indices = [int(index.strip()) for index in ranked_indices_str]    # extract explanation    explanation = result[end_idx + 1 :].strip()    return ranked_indices, explanation


ランク付け後の画像インデックスと最良の結果の理由を取得します。


 ranked_indices, explanation = generate_ranking_explanation(    combined_image_path, query_text )


4. 最良の結果を説明とともに表示する


print(explanation) best_index = ranked_indices[0] best_img = Image.open(retrieved_images[best_index]) best_img = best_img.resize((150, 150)) best_img.show()


結果:


 Reasons: The most suitable item for the user's query intent is index 6 because the instruction specifies a phone case with the theme of the image, which is a leopard. The phone case with index 6 has a thematic design resembling the leopard pattern, making it the closest match to the user's request for a phone case with the image theme. 



ヒョウ柄のスマホケース - ベスト結果


このノートブックの完全なコードを確認してください。このチュートリアルでオンラインデモを開始する方法の詳細については、 サンプルアプリケーション

結論

このブログ記事では、マルチモーダルRAGシステムの構築について説明しました。ミルバス(オープンソースのベクター データベース)。開発者が Milvus を設定し、画像データを読み込み、類似性検索を実行し、LLM を使用して取得した結果を再ランク付けして、より正確な応答を得る方法について説明しました。


マルチモーダル RAG ソリューションは、複数の形式のデータを簡単に理解して処理できる AI システムにさまざまな可能性をもたらします。一般的な可能性としては、画像検索エンジンの改善、コンテキストに基づく結果の改善などが挙げられます。