Vector Database の解説:AIエンジニアのためのPinecone、Weaviate、ChromaDB

AI tutorial - IT technology blog
AI tutorial - IT technology blog

AIアプリケーションにおける従来の検索の課題

現代のAIアプリケーション、特に大規模言語モデル(LLM)や検索拡張生成(RAG)を伴うものに取り組む際、従来のデータストレージはその限界に達することがよくあります。大規模なドキュメントライブラリ向けに設計されたチャットボットを考えてみましょう。

ユーザーが「パスワードをリセットするにはどうすればよいですか?」と尋ねた場合、キーワード検索のみに依存するシステムでは苦戦する可能性があります。「パスワードポリシー」や「アカウント回復」に関する記事が表示されるかもしれませんが、正確なフレーズである「パスワードをリセット」が明示的に存在しない場合、最も関連性の高い解決策を見逃してしまう可能性があります。

この問題はドキュメントに留まりません。eコマースの商品レコメンデーション、法務文書の類似性検索、あるいは類似画像の検索における課題を想像してみてください。標準的なデータベースは、特に完全一致や単純な範囲に基づく構造化クエリでは優れたパフォーマンスを発揮します。しかし、それらはトランザクションデータのために構築されており、言葉の微妙な意味合いや、多様な情報の間の微細な類似性を理解するためには設計されていません。

なぜデータを「理解する」ために新しいアプローチが必要なのか

この限界の根本原因は、コンピューターが伝統的に情報を処理する方法にあります。マシンにとって、「apple」と「orange」は単なる文字列に過ぎません。両方が果物であること、あるいは大文字の「Apple」がテクノロジー企業を指す可能性があることを、本質的に理解しているわけではありません。

ここで埋め込み(embeddings)が登場します。これらは、テキスト、画像、音声、またはその他のあらゆるデータタイプの数値表現(ベクトル)です。

機械学習モデルはこれらのベクトルを生成し、データの意味的意味とコンテキストを捉えるように学習させます。これにより、類似するアイテムは高次元空間で互いに「近く」にクラスター化され、類似しないアイテムは「遠く」に留まります。例えば、「赤い車」の埋め込みは、「バナナ」よりも「青い乗り物」により近くなるでしょう。

すると問題は次のように変化します。これらの数百万、あるいは数十億もの高次元ベクトルを効率的に保存し、クエリを実行して「最も近い」ものを見つけるにはどうすればよいでしょうか?リレーショナルデータベース(PostgreSQLなど)であろうとNoSQLデータベース(MongoDBなど)であろうと、従来のデータベースはこのタスクのために設計されていません。それらのインデックス戦略は、スカラー値の完全一致または範囲クエリに最適化されています。

「近傍探索」(クエリベクトルに最も類似するベクトルを見つけること)を実行するには、クエリをすべての単一ベクトルと比較する必要があります。このアプローチは、大規模なデータセットの場合、計算コストが急速に増大し、数時間または数日かかる可能性さえあります。これこそが、専門的なツールが不可欠である理由です。

ソリューションの比較:Pinecone、Weaviate、ChromaDB

ここでVector Databaseの出番です。これらの専用システムは、ベクトル埋め込みを効率的に保存、インデックス化、クエリします。多くの場合、近似近傍探索(ANN)アルゴリズムを使用して、類似するベクトルを迅速に見つけます。3つの主要なオプションを見てみましょう。

Pinecone:本番環境向けのマネージドな主力製品

Pineconeは、フルマネージドのクラウドネイティブなVector Databaseです。大規模で高性能なAIアプリケーション、特にリアルタイムのセマンティック検索とRAG機能を必要とするもの向けに設計されています。インフラストラクチャを管理する必要はなく、インデックスをプロビジョニングしてベクトルをプッシュするだけです。

  • 強み:
    • スケーラビリティとパフォーマンス: 大規模なデータセットと低遅延クエリ向けに設計されており、1秒あたり数百万のクエリを処理することがよくあります。
    • マネージドサービス: インフラストラクチャ管理が不要なため、アプリケーションロジックに集中できます。
    • 堅牢な機能: フィルタリング、ネームスペース、およびさまざまなインデックスアルゴリズム(例:積量子化、HNSW)をサポートしています。
  • 弱み:
    • コスト: 使用量が増えるにつれて高価になる可能性があります。例えば、高スループットの大規模なインデックスは、月に数百ドルから数千ドルかかる場合があります。
    • 制御の制限: マネージドサービスであるため、セルフホスティングと比較して、基盤となるインフラストラクチャや最適化に対するきめ細かい制御が少なくなります。
  • 最適な用途: プロダクショングレードのRAGシステム、大規模なセマンティック検索、インフラストラクチャ管理を任せたい、厳格なパフォーマンスと信頼性要件を持つAIアプリケーション。

Weaviate:豊富な機能を備えたオープンソースの柔軟性

WeaviateはオープンソースのクラウドネイティブなVector Databaseで、セルフホストまたはマネージドサービスとして使用できます。そのGraphQL APIと、ベクトルと元のデータオブジェクトの両方を一緒に保存できる機能が際立っており、強力なセマンティック検索や、ハイブリッド検索(ベクトル検索とキーワード検索の組み合わせ)も可能にします。

  • 強み:
    • オープンソース: セルフホストを選択した場合、完全な制御と柔軟性を提供します。
    • セマンティック&ハイブリッド検索: GraphQLネイティブクエリを含む、データの深い理解を必要とするアプリケーションに最適です。
    • データとベクトルの保存: 元のデータをそのベクトルと一緒に保存し、データ管理を簡素化します。
    • モジュラーで拡張可能: 埋め込みを生成するための様々なMLモデルと連携しやすいです。
  • 弱み::
    • 複雑性: 大規模でのセルフホスティングは、運用上のオーバーヘッドを招く可能性があります。
    • 学習曲線: GraphQL APIは強力ですが、初めて使用する人には習得に時間がかかる場合があります。
  • 最適な用途:: 高度なセマンティック検索、ハイブリッド検索、マルチモーダルデータを必要とするアプリケーション、またはスケーラブルなオープンソースソリューションの柔軟性が必要な場合。

ChromaDB:組み込み可能で軽量なオプション

ChromaDBはもう一つのオープンソースのVector Databaseですが、非常に軽量で組み込み可能である点が異なります。Pythonアプリケーション内で直接実行できるため、ローカル開発、迅速なプロトタイピング、小規模なアプリケーションに最適です。

  • 強み:
    • シンプルさと使いやすさ: 特にPython開発者にとって、非常に簡単に始められます。
    • 組み込み可能: インメモリで実行したり、データをローカルに永続化したりできるため、開発や小規模プロジェクトに最適です。
    • 統合: LangChainやLlamaIndexといった人気のあるLLMフレームワークにおいて、第一級の存在です。
    • 費用対効果: 無料のオープンソースであり、ローカルでの使用に必要なリソースは最小限です。
  • 弱み:
    • スケーラビリティ: Pineconeや大規模なWeaviateデプロイメントのような、大規模で分散されたスケール向けには設計されていません。
    • 高度な機能の少なさ: より堅牢な製品と比較して、高度な運用機能やインデックス最適化の一部が不足しています。
  • 最適な用途: ローカル開発、概念実証、小規模から中規模のRAGアプリケーション、および迅速なイテレーションのためにインプロセスまたは簡単にセルフホストできるソリューションが必要な場合。

Vector Database実装のための「最善のアプローチ」の選択

Vector Databaseを採用する際、「最善」のアプローチは、プロジェクトの規模、複雑さ、リソースの制約に大きく依存します。私の実務経験からすると、これは習得すべき必須スキルの一つです。どのツールがどの問題に適しているかを知ることです。通常、小規模から始まり、ツールのニーズは進化していきます。

AI開発で一般的な言語であるPythonを使用した実践的な例をいくつか見ていきましょう。まず、埋め込みを生成する必要があります。ここではシンプルな `sentence-transformers` の例を使用しますが、本番環境ではOpenAICohere、または他の埋め込みプロバイダーを使用するかもしれません。

埋め込みの生成(すべてに共通)

どのVector Databaseとやり取りする前でも、データをベクトル形式にする必要があります。


from sentence_transformers import SentenceTransformer

# 事前学習済みの sentence transformer モデルをロード
model = SentenceTransformer('all-MiniLM-L6-v2')

documents = [
    "The quick brown fox jumps over the lazy dog.",
    "Artificial intelligence is transforming industries.",
    "Machine learning is a subset of AI.",
    "A canine rests under a tree."
]

# ドキュメントの埋め込みを生成
document_embeddings = model.encode(documents)

print(f"最初のドキュメントの埋め込み (最初の5次元): {document_embeddings[0][:5]}")
print(f"埋め込みの形状: {document_embeddings.shape}")

ChromaDBの使用(ローカル&シンプル)

ChromaDBは、素早く始めるのに最適です。最小限のセットアップで、完全にインメモリで実行することも、データをディスクに永続化することもできます。


import chromadb
import numpy as np

# オプション1:インメモリクライアント
client = chromadb.Client() 

# オプション2:永続クライアント(データはディスクに保存されます)
# client = chromadb.PersistentClient(path="./chroma_data")

collection_name = "my_rag_collection"

# 再実行時のエラーを避けるためにコレクションが存在するかを確認
try:
    collection = client.get_collection(collection_name)
except:
    collection = client.create_collection(collection_name)

# ドキュメントとその埋め込みをコレクションに追加
# IDが一意の文字列であることを確認
ids = [f"doc_{i}" for i in range(len(documents))]

collection.add(
    embeddings=document_embeddings.tolist(), # Chromaはnumpy配列ではなくリストを期待します
    documents=documents,
    metadatas=[{"source": "例"}] * len(documents),
    ids=ids
)

# 類似ドキュメントをクエリ
query_text = "AIと学習機械"
query_embedding = model.encode([query_text])

results = collection.query(
    query_embeddings=query_embedding.tolist(),
    n_results=2
)

print("\nChromaDB クエリ結果:")
for i, doc in enumerate(results['documents'][0]):
    print(f"  結果 {i+1}: {doc}")

Pineconeの使用(クラウド&スケーラブル)

Pineconeでは、ダッシュボードからAPIキーと環境が必要です。この例では、正しい次元(例:384の場合all-MiniLM-L6-v2)とメトリック(例:'cosine')で'my-index'という名前のインデックスをセットアップ済みであることを前提としています。


from pinecone import Pinecone, Index, PodSpec
import os

# Pineconeを初期化(実際のAPIキーと環境に置き換えてください)
api_key = os.getenv("PINECONE_API_KEY")
environment = os.getenv("PINECONE_ENVIRONMENT") 

if not api_key or not environment:
    print("PINECONE_API_KEY と PINECONE_ENVIRONMENT 環境変数を設定する必要があります。")
    # 実際のアプリケーションでは、これをより堅牢に処理します
else:
    pc = Pinecone(api_key=api_key)

    index_name = "my-index"
    dimension = document_embeddings.shape[1] # 例: all-MiniLM-L6-v2 の場合は 384
    metric = "cosine"

    # インデックスが存在しない場合は作成
    if index_name not in pc.list_indexes():
        pc.create_index(
            index_name,
            dimension=dimension,
            metric=metric,
            spec=PodSpec(environment=environment)
        )

    index = pc.Index(index_name)

    # upsert 用にデータを準備
    # Pinecone は (id, vector, metadata) のタプルリストを期待します
    vectors_to_upsert = []
    for i, (doc_embedding, doc_text) in enumerate(zip(document_embeddings, documents)):
        vectors_to_upsert.append((
            f"doc_{i}", 
            doc_embedding.tolist(), 
            {"text": doc_text, "source": "例"}
        ))
    
    index.upsert(vectors=vectors_to_upsert)

    # 類似ドキュメントをクエリ
    query_embedding = model.encode([query_text]).tolist()[0]

    pinecone_results = index.query(
        vector=query_embedding,
        top_k=2,
        include_metadata=True
    )

    print("\nPinecone クエリ結果:")
    for match in pinecone_results['matches']:
        print(f"  スコア: {match['score']:.2f}, ドキュメント: {match['metadata']['text']}")

Weaviateの使用(セルフホストまたはクラウド)

WeaviateはDockerを介してローカルで実行することも、マネージドクラウドサービスとして使用することもできます。この例では、ローカルのWeaviateインスタンスが実行されていることを前提としています(例:`docker run -p 8080:8080 -p 50051:50051 semeru/weaviate:latest`)。


import weaviate
from weaviate.util import get_valid_uuid

# Weaviateに接続(例:ローカルインスタンス)
client = weaviate.Client("http://localhost:8080")

# データスキーマを定義
class_name = "MyDocument"

# 新しい実行のためにスキーマを削除して再作成したい場合:
# if client.schema.exists(class_name):
#     client.schema.delete(class_name)

if not client.schema.exists(class_name):
    my_class_schema = {
        "class": class_name,
        "description": "ドキュメントとその埋め込みを保存するクラス",
        "vectorizer": "none", # 独自の埋め込みを提供
        "properties": [
            {
                "name": "text",
                "dataType": ["text"],
                "description": "元のドキュメントテキスト"
            },
            {
                "name": "source",
                "dataType": ["text"],
                "description": "ドキュメントのソース"
            }
        ]
    }
    client.schema.create_class(my_class_schema)

# 埋め込み付きデータオブジェクトを追加
with client.batch as batch:
    for i, (doc_embedding, doc_text) in enumerate(zip(document_embeddings, documents)):
        data_object = {"text": doc_text, "source": "例"}
        batch.add_data_object(
            data_object,
            class_name,
            vector=doc_embedding.tolist() # Weaviateはリストを期待します
        )

# 類似ドキュメントをクエリ
query_embedding = model.encode([query_text]).tolist()

weaviate_results = client.query.get(
    class_name, ["text", "source"]
).with_near_vector({"vector": query_embedding[0]}).with_limit(2).do()

print("\nWeaviate クエリ結果:")
if 'data' in weaviate_results and 'Get' in weaviate_results['data'] and class_name in weaviate_results['data']['Get']:
    for item in weaviate_results['data']['Get'][class_name]:
        print(f"  ドキュメント: {item['text']}")

選択の要約:

  • ChromaDB: 迅速なプロトタイピング、ローカル開発、および小規模で組み込み可能なアプリケーションに最適です。Pythonワークフローへの統合が非常に簡単です。
  • Weaviate: Chromaよりも多くの機能が必要な場合、セルフホストまたは柔軟なマネージドサービスを使用したい場合、その強力なセマンティックおよびハイブリッド検索機能、多くの場合データ共存の恩恵を受けたい場合に適しています。
  • Pinecone: スケーラビリティ、高パフォーマンス、最小限の運用オーバーヘッドが最重要であり、フルマネージドのエンタープライズグレードソリューションを求める、要求の厳しい本番環境向けです。

これらのVector Databaseはそれぞれ、機能、スケーラビリティ、運用上の複雑さにおいて独自のバランスを提供します。それぞれの強みを理解することで、目的に合った適切なツールを選択できます。これにより、AIアプリケーションが情報を効果的に処理および取得し、より良い結果を導き出すことができます。

Share: