Java開発者のためのPython実践ガイド:ベクトルデータベースを活用した画像類似検索

本日は、ベクトルデータベースを利用した画像類似検索の実装方法について解説します。当初は画像や動画をベクトルに変換してベクトルデータベースに保存する予定でしたが、Tencentのベクトルデータベースが完全に開発段階ではないため、今回はテキストベースの画像類似検索をデモンストレーションします。

インタラクティブなアプリケーション構築に役立つツールとしてgradioをご紹介します。このライブラリは、機械学習モデルやAPI、データサイエンスのワークフローを共有する際に非常に便利です。gradioを使用すれば、数行のコードでブラウザ上で動作するインタラクティブなデモアプリケーションを構築できます。

バックエンド開発者として、フロントエンドの実装に多くの時間を割きたくないという方も多いでしょう。gradioは、インターフェース部分の実装を気にすることなく、ビジネスロジックに集中できる優れたソリューションを提供します。

ソースコードリポジトリ: https://github.com/StudiousXiaoYu/tx-image-search

Gradio概要

gradioの環境設定や公式ドキュメントの詳細については割愛します。興味のある方は公式ドキュメント(https://www.gradio.app/guides/quickstart)を参照してください。バックエンド開発者にとってgradioの導入は比較的容易です。

ここでは、最も基本的な画像表示アプリケーションを構築します。画像表示機能を実装するため、以下のコードを直接示します。

データ準備

まず、データを準備する必要があります。公式から取得した訓練データをベクトルデータベースに保存し、画像の情報とパスをCSVファイルに整理しました。このデータをデータベースに挿入する機会に、Pythonの基本的な文法、サードパーティパッケージの使用、ループ処理などを練習してみましょう。

CSVファイルの形式は以下の通りです:

id,path,label
0,./train/brain_coral/n01917289_1783.JPEG,brain_coral
1,./train/brain_coral/n01917289_4317.JPEG,brain_coral
2,./train/brain_coral/n01917289_765.JPEG,brain_coral
3,./train/brain_coral/n01917289_1079.JPEG,brain_coral
4,./train/brain_coral/n01917289_2484.JPEG,brain_coral
5,./train/brain_coral/n01917289_1082.JPEG,brain_coral
6,./train/brain_coral/n01917289_1538.JPEG,brain_coral

このファイルの最初の行は列名で、2行目からデータの解析を開始できます。

データベースの作成は既に完了しているため、ここではコレクションの設計とデータ挿入に焦点を当てます。

import gradio as gr
import numpy as np
import tcvectordb
from tcvectordb.model.collection import Embedding
from tcvectordb.model.document import Document, Filter, SearchParams
from tcvectordb.model.enum import FieldType, IndexType, MetricType, ReadConsistency, EmbeddingModel
from tcvectordb.model.index import Index, VectorIndex, FilterIndex, HNSWParams

# ベクトルデータベースクライアントの初期化
db_client = tcvectordb.VectorDBClient(
    url='http://*****',
    username='root', 
    key='1tWQ*****',
    read_consistency=ReadConsistency.EVENTUAL_CONSISTENCY, 
    timeout=30
)
db = db_client.database('db-xiaoyu')

これらの基本的な接続手続きは割愛し、画像検索用の専用コレクションを作成します。以前にテキスト検索用のコレクションを作成しましたが、今回は新しいコレクションを作成して区別します。

# インデックス設定
index_config = Index(
    FilterIndex(name='id', field_type=FieldType.String, index_type=IndexType.PRIMARY_KEY),
    VectorIndex(name='vector', dimension=768, index_type=IndexType.HNSW,
                metric_type=MetricType.COSINE, params=HNSWParams(m=16, efconstruction=200))
)

# 埋め込み設定
embedding_config = Embedding(
    vector_field='vector', 
    field='image_info', 
    model=EmbeddingModel.BGE_BASE_ZH
)

# コレクションの作成
image_collection = db.create_collection(
    name='image-xiaoyu',
    shard=1,
    replicas=0,
    description='画像検索用のテスト埋め込みコレクション',
    embedding=embedding_config,
    index=index_config
)

現在、ベクトルデータベースは画像ファイルをベクトルに変換する機能を完全にはサポートしていないため、画像の説明情報を保存し、画像パスを通常のフィールドとして保存することにしました。パスにフィルタリング要件がないため、通常のフィールドとして保存します。すべての情報はCSVファイルに保存されているため、その内容を直接読み取ってベクトルデータベースに保存するだけです。

# CSVファイルからデータを読み込む
image_data = np.genfromtxt('./reverse_image_search/reverse_image_search.csv', 
                          delimiter=',', 
                          skip_header=1, 
                          usecols=[0, 1, 2], 
                          dtype=None)

# ドキュメントリストの作成
document_list = []
for row in image_data:
    doc_id = str(row[0])
    image_path = row[1].decode()
    image_description = row[2].decode()
    document_list.append(
        Document(
            id=doc_id,
            image_path=image_path,
            image_description=image_description
        )
    )

# コレクションにドキュメントを挿入
insert_result = image_collection.upsert(
    documents=document_list,
    build_index=True
)

ここではNumPyライブラリを使用してCSVファイルを処理しています。Pythonでは既存のツールを活用することが一般的です。最後に、Documentをリストに格納してコレクションに挿入します。

Gradioインターフェースの構築

データの準備が完了したら、インタラクティブなインターフェースを構築する方法を考えます。Pythonには優れたライブラリが多数存在し、その中にはワンクリックでインタラクティブなインターフェースを構築できるものもあります。Javaのカスタムインターフェースとは比較にならないほど簡単です。インタラクティブなインターフェースを実現するために、新しいPythonファイルに以下のコードを記述します。

import gradio as gr
import tcvectordb
from tcvectordb.model.document import SearchParams
from tcvectordb.model.enum import ReadConsistency

# ベクトルデータベースクライアントの初期化
db_client = tcvectordb.VectorDBClient(
    url='http://lb-m*****',
    username='root', 
    key='1tWQ*****',
    read_consistency=ReadConsistency.EVENTUAL_CONSISTENCY, 
    timeout=30
)
db = db_client.database('db-xiaoyu')
image_collection = db.collection('image-xiaoyu')


def search_by_text(query_text):
    """テキストによる画像検索"""
    search_results = image_collection.searchByText(
        embeddingItems=[query_text],
        params=SearchParams(ef=200),
        limit=3,
        retrieve_vector=False,
        output_fields=['image_path', 'image_description']
    )
    
    # 結果から画像パスを抽出
    matched_images = []
    for result_group in search_results.get("documents"):
        for doc in result_group:
            matched_images.append(str(doc["image_path"]))
    
    return matched_images


def search_by_image(image_input):
    """画像による類似検索(実装予定)"""
    # この機能は今後の実装を予定
    pass


# Gradioインターフェースの定義
with gr.Blocks() as demo_app:
    gr.Markdown("このデモはテキストまたは画像ファイルを使用して類似画像を検索します。")
    
    # テキスト検索タブ
    with gr.Tab("テキスト検索"):
        with gr.Row():
            text_input = gr.Textbox(label="検索キーワードを入力")
            image_output = gr.Gallery(label="検索結果", columns=3, height='auto')
        search_button = gr.Button("検索開始")
    
    # 画像検索タブ
    with gr.Tab("画像検索"):
        with gr.Row():
            image_input = gr.Image(label="検索用画像")
            image_result = gr.Gallery(label="検索結果", columns=3, height='auto')
        image_search_button = gr.Button("検索開始")

    # 情報パネル
    with gr.Accordion("プロジェクト情報"):
        gr.Markdown("1. 画像とその説明をベクトルデータベースに保存\n2. テキストまたは画像を使用して類似画像を検索")

    # イベントハンドラの登録
    search_button.click(search_by_text, inputs=text_input, outputs=image_output)
    image_search_button.click(search_by_image, inputs=image_input, outputs=image_result)

# アプリケーションの起動
demo_app.launch()

2つのタブを持つインターフェースを作成しました。このプロジェクトでは画像類似検索機能は必要ないため、将来的に機能が追加された際に実装する予定です。現時点では、画像の説明に基づいて画像を検索して表示するのみです。これはGradioの公式サンプルを少し変更したものです。詳細については公式サンプルやドキュメントを参照することをお勧めします。

"gold"と入力すると、保存した画像の説明が"gold fish"であるため、対応する一致項目が見つかります。3種類の金魚の画像が表示されれば、正常に動作しています。画像類似検索のための部分も確保しており、機能が追加された際に迅速に更新できるように準備しています。

まとめ

本日のコード実装を通じて、Pythonの基本的な構文はほぼ習得できたかと思います。残るはサードパーティパッケージの使用方法を学び、不明なパッケージに遭遇した際には検索して解決策を見つけるスキルを身につけることです。大きな難題はありませんが、gradioの使用にはある程度の時間を要するかもしれません。エラーが発生した場合、Javaのように問題をすぐに特定できるわけではなく、検索して解決する必要があります。

タグ: Python ベクトルデータベース Gradio 類似検索 Tencent

7月23日 00:48 投稿