📚

なぜあなたの画像検索は失敗するのか(ColPali + マルチベクトルインデックスで解決する方法)

に公開

問題点:従来の画像検索がなぜ失敗するのか

従来の画像検索がなぜ失敗するのか

画像検索システムを構築しようとしたことがあれば、その苦労を知っているでしょう。従来のアプローチでは、画像全体を1つの密なベクトルに圧縮します。つまり、複雑な視覚シーンを高次元空間の1点に押し込めているのです。

何が失われるのでしょうか?

  • 空間的なレイアウトと位置情報
  • 散らばったシーン内の複数のオブジェクト
  • チャート、図、テキスト領域などの細かいディテール
  • 精密なマッチングに重要なローカルセマンティックコンテキスト

技術マニュアルで「データベースアーキテクチャを示す図」を検索することを想像してください。グローバルベクトルでは、その図がページのどこにあるかを特定したり、他の視覚要素と区別したりすることができません。あいまいで不正確なマッチングしか得られないのです。

ColPaliの登場:パッチレベルのマルチベクトルインデックス

**ColPali(Contextual Late-interaction over Patches)**は、視覚検索を根本から再考します。画像ごとに1つのベクトルではなく、数百から数千のパッチレベル埋め込みを生成し、空間構造とセマンティックな豊かさを保持します。

仕組み

  1. 画像の分解:各画像を格子状に分割(例:32×32パッチ = ページあたり1,024パッチ)
  2. パッチ埋め込み:各パッチがビジョン言語モデルを使用して独自のコンテキスト埋め込みを取得
  3. Late Interaction:クエリ時に、テキストクエリトークンがすべてのパッチ埋め込みと照合される
  4. MaxSimスコアリング:各クエリトークンについて、すべてのパッチで最大類似度のみを保持し、これらのスコアを合計

これはColBERTのlate interactionパラダイムに触発されていますが、マルチモーダル視覚検索に適応されています。

なぜ重要なのか

🎯 細粒度検索:グローバルセマンティクスだけでなく、特定の領域にマッチ
🏗️ 構造の保持:空間関係とレイアウト情報がそのまま残る
📊 より良い再現率:密な視覚シーンでも小さくて重要な領域を「忘れない」
効率的な検索:Late interactionはインデックス時の高コストなクロスアテンションを回避
🚫 OCR不要:エラーが発生しやすいテキスト抽出なしで画像をネイティブに処理

CocoIndex + Qdrantでの構築

構築するアーキテクチャは以下の通りです:

画像 → ColPali埋め込み → マルチベクトルストレージ(Qdrant) → Late Interaction検索

ステップ1:画像の取り込み

@cocoindex.flow_def(name="ImageObjectEmbeddingColpali")
def image_object_embedding_flow(flow_builder, data_scope):
    data_scope["images"] = flow_builder.add_source(
        cocoindex.sources.LocalFile(
            path="img",
            included_patterns=["*.jpg", "*.jpeg", "*.png"],
            binary=True
        ),
        refresh_interval=datetime.timedelta(minutes=1),
    )

これはローカルディレクトリを監視し、新しい画像が到着すると毎分自動更新します。

ステップ2:ColPaliで埋め込み

img_embeddings = data_scope.add_collector()

with data_scope["images"].row() as img:
    img["embedding"] = img["content"].transform(
        cocoindex.functions.ColPaliEmbedImage(
            model="vidore/colpali-v1.2"
        )
    )

各画像がマルチベクトル表現になります:Vector[Vector[Float32, N]]

ここで:

  • 外側の次元 = パッチ数(例:1024)
  • 内側の次元 = モデルの隠れサイズ(例:128)

ステップ3:Qdrantに保存

collect_fields = {
    "id": cocoindex.GeneratedField.UUID,
    "filename": img["filename"],
    "embedding": img["embedding"],
}

img_embeddings.collect(**collect_fields)

img_embeddings.export(
    "img_embeddings",
    cocoindex.targets.Qdrant(collection_name="ImageSearchColpali"),
    primary_key_fields=["id"],
)

Qdrantはマルチベクトルフィールドをネイティブサポートしており、ColPaliのパッチベースアプローチに最適です。

ステップ4:リアルタイムインデックス

@asynccontextmanager
async def lifespan(app: FastAPI):
    load_dotenv()
    cocoindex.init()
    image_object_embedding_flow.setup(report_to_stdout=True)
    
    app.state.live_updater = cocoindex.FlowLiveUpdater(
        image_object_embedding_flow
    )
    app.state.live_updater.start()
    yield

画像が追加、変更、削除されると、インデックスがリアルタイムで同期されます。

インデックスのクエリ

@app.get("/search")
def search(
    q: str = Query(..., description="検索クエリ"),
    limit: int = Query(5, description="結果数"),
) -> Any:
    # クエリのマルチベクトル埋め込み
    query_embedding = text_to_colpali_embedding.eval(q)
    
    # QdrantでLate Interaction検索
    results = qdrant_client.search(
        collection_name="ImageSearchColpali",
        query_vector=query_embedding,
        limit=limit
    )
    
    return results

パフォーマンスの違い

単一ベクトルアプローチ(CLIPなど)と比較して、ColPaliは以下を実現:

より豊かな検索:微妙な視覚的詳細を捉える
より良いローカライゼーション:複雑なシーンで特定の領域を識別
より高い再現率:小さくても重要な要素を見逃さない
解釈可能性:MaxSimスコアがどのパッチがどのクエリトークンにマッチしたかを示す

ローカルファイル以外:任意のデータソースに接続

CocoIndexは本番対応のソースコネクタをサポート:

  • Google Drive:ドキュメントと画像を自動同期
  • Amazon S3/SQS:スケールでのイベント駆動インデックス
  • Azure Blob Storage:エンタープライズクラウド統合

変更は自動的に検出され、リアルタイムでインデックスに反映されます。手動での再構築は不要です。

ユースケース

🔍 Visual RAG:ドキュメントレイアウトを理解するAIエージェントを構築
📚 ドキュメント検索:マニュアル内の特定のチャート、表、図を検索
🏥 医療画像:解剖学的特徴で放射線レポートを検索
🛍️ Eコマース:細粒度の製品画像検索
🎨 デジタルアセット管理:視覚的構成でデザインファイルを検索

重要な技術詳細

ストレージ形式

Vector[Vector[Float32, embedding_dim]]
  • 各画像 = パッチベクトルの配列
  • Late interaction戦略を可能にする
  • 量子化と圧縮(HPC-ColPali)に対応

Late Interactionスコアリング

score = Σ max(sim(query_token_i, patch_j)) すべてのパッチjについて
  • 高コストな結合エンコーディングを回避
  • スケールで効率的な検索を可能に
  • 解釈可能性を保持

スケーリング戦略

  • 量子化:精度の損失を最小限に抑えて埋め込みを圧縮
  • 階層的パッチ圧縮:ストレージニーズをさらに削減
  • 分散インデックス:数十億の画像にスケール

自分で試してみる

完全な動作コード:github.com/cocoindex-io/cocoindex/tree/main/examples/image_search

pip install cocoindex
# 例を実行
python examples/image_search/colpali_main.py

なぜこれが本番環境で重要なのか

従来の画像検索は以下が必要な場合に失敗します:

  • 複雑なシーンでの精密なローカライゼーション
  • 複数オブジェクトの理解
  • レイアウトを意識した検索
  • 変化するデータソースとのリアルタイム同期

ColPali + CocoIndexは、これらすべてを処理する本番対応の基盤を提供します。わずか数行の宣言的Pythonで実現できます。


マルチモーダルAIシステムを構築している方は、GitHubでCocoIndexにスターをつけてくださいgithub.com/cocoindex-io/cocoindex

質問がありますか?Discordコミュニティに参加するか、ドキュメントをご確認ください。


次世代のマルチモーダル検索向けAIインフラを構築していますか?CocoIndexはあなたが探していた欠けているピースです。

Discussion