Chroma とは何か? 🤔
Chroma(またはChroma DB)は、AIアプリケーション、特に大規模言語モデル(LLM)を活用したアプリケーション開発を加速するために設計された、オープンソースのAIネイティブなベクトルデータベースです。
テキスト、画像、音声などの非構造化データを「埋め込み(Embeddings)」と呼ばれる数値ベクトルに変換し、それらを効率的に保存、管理、検索することに特化しています。これにより、従来のデータベースでは難しかった、意味的な類似性に基づいた高速な検索(セマンティック検索)が可能になります。
主な目的は以下の通りです。
- 埋め込みベクトルとそのメタデータを効率的に保存・管理する
- セマンティック検索やLLMアプリケーション開発を容易にする
PythonやJavaScript/TypeScript用のSDKが提供されており、開発者は使い慣れた言語で簡単にChromaを利用できます。Apache 2.0ライセンスの下で公開されているため、誰でも自由に使用、改変、配布することが可能です。
Chroma の主な特徴 ✨
Chromaは、開発者がAIアプリケーションを迅速に構築できるよう、多くの強力な機能を備えています。
- シンプルさ: APIは非常にシンプルで、主要な機能はわずか4つの関数で構成されています。ドキュメントも整備されており、学習コストが低いのが特徴です。
- AIネイティブ設計: 埋め込みの生成、保存、検索といった、AIアプリケーション特有のニーズに対応できるように設計されています。トークン化、埋め込み生成、インデックス作成などを自動的に処理できます。
- 豊富な機能:
- ベクトル検索(類似性検索)
- メタデータに基づいたフィルタリング
- ドキュメントストレージ
- 全文検索
- マルチモーダル対応(テキストだけでなく画像なども扱える)
- 密度の推定など、高度な機能も提供
- 統合性: LangChainやLlamaIndexといった主要なLLM開発フレームワークとの統合が容易です。HuggingFace、OpenAI、Googleなどの埋め込みモデルとの連携もスムーズです。
- 柔軟なデプロイメント:
- インメモリモード: Pythonスクリプト内で完結し、手軽に試せます。プロトタイピングに最適ですが、プログラム終了時にデータは失われます。
- 永続化モード: ローカルファイルシステムにデータを保存し、プログラムを再起動してもデータが保持されます。
- クライアント/サーバーモード: Chromaサーバーを別途起動し、PythonやJavaScriptのクライアントから接続します。これにより、複数のアプリケーションからデータベースを共有したり、よりスケーラブルな構成を組んだりできます。Dockerを使ったデプロイも容易です。
- オープンソース: Apache 2.0ライセンスで提供されており、商用利用も含めて自由に利用できます。GitHub上で活発に開発が進められています。
- 多言語対応: Python、JavaScript/TypeScriptの公式SDKに加え、Ruby、PHP、Javaなどの言語向けSDKも存在します。
コアコンセプト:埋め込みとベクトルデータベース 🔢
Chromaを理解する上で重要な概念が「埋め込み(Embeddings)」と「ベクトルデータベース(Vector Database)」です。
埋め込み (Embeddings) とは?
埋め込みとは、テキスト、画像、音声といった非構造化データを、機械学習モデルが理解できるような低次元の数値ベクトルに変換するプロセス、またはその結果得られるベクトルそのものを指します。
例えるなら、データの本質的な「意味」や「特徴」を捉えた数値のリスト(座標のようなもの)です。このプロセスにより、似た意味を持つデータはベクトル空間上で互いに「近く」に配置され、意味的に異なるデータは「遠く」に配置されます。
例えば、「サンフランシスコの有名な橋」というテキストクエリを埋め込みベクトルに変換し、写真とそのメタデータの埋め込みベクトルと比較することで、「ゴールデンゲートブリッジ」の写真を見つけ出すことができます。
ChromaはデフォルトでSentence Transformersなどのライブラリを使ってテキストの埋め込みを自動生成しますが、OpenAI APIやCohere、あるいは自前で用意した埋め込みモデルやベクトルを利用することも可能です。
ベクトルデータベース (Vector Database) とは?
ベクトルデータベースは、これらの埋め込みベクトルを効率的に保存し、高速に検索するための専用データベースです。
従来のデータベース(リレーショナルデータベースなど)がキーワードの一致や特定の条件に基づいてデータを検索するのに対し、ベクトルデータベースはベクトル間の「近さ(類似度)」に基づいて検索を行います。この検索方法は近似最近傍探索(Approximate Nearest Neighbor, ANN)と呼ばれ、大量のベクトルデータの中からでも高速に関連性の高いデータを見つけ出すことができます。
Chromaは、このベクトルデータベースとしての機能を提供し、埋め込みの保存、管理、そして類似性検索を容易にします。
インストール 💻
ChromaのPythonライブラリ(chromadb
)はpipを使って簡単にインストールできます。
pip install chromadb
もし最新の開発版を試したい場合は、GitHubリポジトリから直接インストールすることも可能です。
pip install git+https://github.com/chroma-core/chroma.git@main
特定のバージョンを指定してインストールすることもできます。
pip install chromadb==<x.y.z>
JavaScript/TypeScriptで使用する場合は、npmまたはyarnを使います。
npm install chromadb
yarn add chromadb
クライアント/サーバーモードで利用する場合は、サーバーを起動する必要があります。
chroma run --path /path/to/persist/data
このコマンドは、指定したパスにデータを永続化するChromaサーバーを起動します。
基本的な使い方 (Python) 🐍
Chromaの基本的な使い方は非常にシンプルです。ここではインメモリモードでの簡単な例を示します。
1. Chroma クライアントの作成
まず、Chromaクライアントオブジェクトを作成します。インメモリモードでは、引数なしでClient()
を呼び出します。
import chromadb
# インメモリクライアントを作成
client = chromadb.Client()
print("Chromaクライアントを作成しました。")
2. コレクションの作成
コレクションは、埋め込み、ドキュメント、メタデータを格納する場所です。テーブルのようなものだと考えてください。create_collection()
メソッドで作成します。同じ名前のコレクションが存在しない場合は作成し、存在する場合はエラーになります。get_or_create_collection()
を使うと、存在すればそれを取得し、なければ作成します。
# "my_documents" という名前のコレクションを作成(または取得)
collection_name = "my_documents"
try:
collection = client.create_collection(name=collection_name)
print(f"コレクション '{collection_name}' を作成しました。")
except Exception as e:
print(f"コレクション '{collection_name}' の作成に失敗しました: {e}")
# 既に存在する場合などに備えてget_or_create_collectionを使うのが一般的
collection = client.get_or_create_collection(name=collection_name)
print(f"既存のコレクション '{collection_name}' を取得しました。")
コレクションを作成する際に、使用する埋め込み関数や距離計算方法(メタデータとしてhnsw:space
で指定、例: “cosine”, “l2”, “ip”)を指定することもできます。
from chromadb.utils import embedding_functions
# 例: Sentence Transformers のモデルを指定する場合
# sentence_transformer_ef = embedding_functions.SentenceTransformerEmbeddingFunction(model_name="all-MiniLM-L6-v2")
# collection = client.get_or_create_collection(
# name="my_documents_with_st",
# embedding_function=sentence_transformer_ef,
# metadata={"hnsw:space": "cosine"} # コサイン類似度を使用
# )
# print("Sentence Transformers を使用するコレクションを作成/取得しました。")
# デフォルトの埋め込み関数を使用する場合(Sentence Transformersのall-MiniLM-L6-v2が使われることが多い)
collection = client.get_or_create_collection(name="my_documents_default")
print("デフォルトの埋め込み関数を使用するコレクションを作成/取得しました。")
3. ドキュメントの追加
add()
メソッドを使って、ドキュメント(テキストデータ)、メタデータ、そしてユニークなIDをコレクションに追加します。Chromaは自動的にドキュメントを埋め込みベクトルに変換して保存します。
try:
collection.add(
documents=[
"これはパイナップルについてのドキュメントです。",
"これはオレンジについてのドキュメントです。",
"ハワイは美しい島です。",
"フロリダはオレンジの産地として有名です。"
],
metadatas=[
{"source": "wiki", "topic": "fruit"},
{"source": "blog", "topic": "fruit"},
{"source": "travel_guide", "topic": "location"},
{"source": "wiki", "topic": "location"}
],
ids=["doc_pine", "doc_orange", "loc_hawaii", "loc_florida"] # 各ドキュメントにユニークなIDを付与
)
print("ドキュメントをコレクションに追加しました。")
except Exception as e:
print(f"ドキュメントの追加中にエラーが発生しました: {e}")
# 既にIDが存在する場合などのエラーハンドリング
metadatas
はオプションですが、後でフィルタリングする際に役立ちます。ids
は必須であり、各ドキュメントを一意に識別するために使われます。
4. コレクションのクエリ(検索)
query()
メソッドを使って、コレクション内のドキュメントを検索します。クエリテキスト(複数可)と、返してほしい結果の数(n_results
)を指定します。
query_text = "ハワイの果物について教えて"
print(f"\nクエリ: '{query_text}'")
results = collection.query(
query_texts=[query_text],
n_results=2 # 最も類似度の高いドキュメントを2つ取得
)
print("検索結果:")
print(results)
# メタデータでフィルタリングする例
query_text_fruit = "フルーツについて"
print(f"\nクエリ(フルーツ限定): '{query_text_fruit}'")
results_filtered = collection.query(
query_texts=[query_text_fruit],
n_results=2,
where={"topic": "fruit"} # topicがfruitのドキュメントのみを対象
)
print("フィルタリング検索結果:")
print(results_filtered)
# ドキュメントの内容でフィルタリングする例 (部分一致)
query_text_orange = "オレンジを含むドキュメント"
print(f"\nクエリ(オレンジを含む): '{query_text_orange}'")
results_doc_filtered = collection.query(
query_texts=["オレンジ"], # クエリ自体はシンプルでもOK
n_results=2,
where_document={"$contains":"オレンジ"} # ドキュメント本文に "オレンジ" を含むもの
)
print("ドキュメント内容フィルタリング検索結果:")
print(results_doc_filtered)
結果には、類似したドキュメントの内容、ID、メタデータ、そしてクエリとの距離(類似度)などが含まれます。最初の例では、「ハワイの果物について教えて」というクエリに対して、「これはパイナップルについてのドキュメントです。」が最も類似度が高いと判断される可能性があります。
5. その他の操作
Chromaには他にもコレクションやデータを操作するためのメソッドがあります。
get()
: IDや条件を指定してドキュメントを取得update()
: 既存のドキュメントやメタデータを更新upsert()
: ドキュメントが存在すれば更新、なければ追加delete()
: IDや条件を指定してドキュメントを削除peek()
: コレクションの先頭のデータをいくつか確認count()
: コレクション内のドキュメント数を取得modify()
: コレクションの名前やメタデータを変更delete_collection()
: コレクション自体を削除
# コレクション内のドキュメント数を取得
count = collection.count()
print(f"\nコレクション '{collection.name}' には {count} 個のドキュメントがあります。")
# IDを指定してドキュメントを取得
doc_retrieved = collection.get(ids=["doc_orange"])
print("\nID 'doc_orange' で取得したドキュメント:")
print(doc_retrieved)
# ドキュメントを更新 (例: メタデータを更新)
# collection.update(
# ids=["doc_pine"],
# metadatas=[{"source": "wiki_updated", "topic": "tropical_fruit"}]
# )
# print("\nドキュメント 'doc_pine' のメタデータを更新しました。")
# ドキュメントを削除 (例: id 'loc_florida' を削除)
# collection.delete(ids=["loc_florida"])
# print("\nドキュメント 'loc_florida' を削除しました。")
# print(f"削除後のドキュメント数: {collection.count()}")
# コレクションを削除(注意:データが完全に失われます)
# client.delete_collection(name=collection_name)
# print(f"\nコレクション '{collection_name}' を削除しました。")
永続化とクライアント/サーバーモード 💾☁️
データの永続化
上記で示したインメモリモード (chromadb.Client()
) は、Pythonスクリプトの実行中のみデータが保持され、終了すると消えてしまいます。データを永続化したい場合は、PersistentClient
を使用し、データの保存場所を指定します。
import chromadb
# データを './chroma_db' ディレクトリに永続化するクライアントを作成
persistent_client = chromadb.PersistentClient(path="./chroma_db")
print("永続化クライアントを作成しました。データは './chroma_db' に保存されます。")
# 以降のコレクション作成や操作は Client と同様
persistent_collection = persistent_client.get_or_create_collection("persistent_docs")
print(f"永続化コレクション '{persistent_collection.name}' を作成/取得しました。")
# 例: データを追加
try:
persistent_collection.add(
documents=["永続化されるデータです。"],
ids=["persist_1"]
)
print("永続化コレクションにデータを追加しました。")
except Exception as e:
print(f"永続化コレクションへのデータ追加エラー: {e}") # ID重複など
print(f"現在のドキュメント数: {persistent_collection.count()}")
この方法なら、スクリプトを再実行しても ./chroma_db
ディレクトリに保存されたデータが読み込まれ、以前の状態から操作を続けることができます。
クライアント/サーバーモード
より本格的なアプリケーションや、複数のプロセス/マシンからデータベースにアクセスしたい場合は、クライアント/サーバーモードを利用します。
- サーバーの起動: ターミナルで以下のコマンドを実行し、Chromaサーバーを起動します。
--path
でデータの保存先を指定します。
(chroma run --path /path/to/your/chroma_server_data --port 8000
--port
はデフォルトで8000です) - クライアントからの接続: Pythonスクリプトからは、
HttpClient
を使用して起動中のサーバーに接続します。import chromadb # 実行中のChromaサーバーに接続するクライアントを作成 # デフォルトでは http://localhost:8000 に接続 http_client = chromadb.HttpClient(host='localhost', port=8000) print("HTTPクライアントを作成し、Chromaサーバーに接続しようとしています...") # サーバーが起動しているか確認 (heartbeat) try: heartbeat = http_client.heartbeat() print(f"サーバーのハートビートを取得しました: {heartbeat}") # サーバーモードでもコレクション操作は同様 server_collection = http_client.get_or_create_collection("server_docs") print(f"サーバー上のコレクション '{server_collection.name}' を作成/取得しました。") # 例: データを追加 try: server_collection.add(documents=["サーバー経由で追加されたデータ"], ids=["server_doc_1"]) print("サーバーコレクションにデータを追加しました。") except Exception as e: print(f"サーバーコレクションへのデータ追加エラー: {e}") # ID重複など print(f"サーバーコレクションのドキュメント数: {server_collection.count()}") except Exception as e: print(f"サーバーへの接続または操作に失敗しました: {e}") print("Chromaサーバーが指定のホスト・ポートで起動しているか確認してください。")
クライアント/サーバーモードでは、データベースの管理とアプリケーションロジックを分離でき、スケーラビリティや可用性の面で有利になります。認証機能を追加することも可能です。
ユースケース 💡
Chromaは、そのベクトル検索能力を活かして、様々なAIアプリケーションで利用されています。
- RAG (Retrieval-Augmented Generation): LLMが回答を生成する際に、外部の知識ソース(Chromaに格納されたドキュメント)から関連性の高い情報を検索し、その情報をコンテキストとしてLLMに与えることで、より正確で信頼性の高い回答を生成させる技術です。「Chat with your data(自分のデータとチャットする)」系のアプリケーションの基盤となります。
- セマンティック検索: キーワードの一致だけでなく、意味的な類似性に基づいてドキュメントや情報を検索します。例えば、製品カタログから「夏向けの涼しい服」といった曖昧なクエリで関連商品を探す、社内ドキュメントから過去の類似事例を検索する、といった用途が考えられます。
- 推薦システム: ユーザーの閲覧履歴や購入履歴などのベクトルに基づいて、類似したアイテムやコンテンツを推薦します。
- 異常検知: 正常なデータのベクトル分布から大きく外れたデータを異常として検出します。
- 重複検出: 大量のドキュメントの中から、意味的に重複しているものを見つけ出します。
- 画像・音声検索: テキストだけでなく、画像や音声データもベクトル化して保存・検索できます。「似た画像を探す」「似た音楽を探す」といった機能を実現できます。
Chromaのシンプルさと強力な機能により、これらのユースケースを比較的容易に実装することができます。
まとめ 🎉
Chromaは、AI時代のアプリケーション開発、特にLLMを活用したシステム構築において、非常に強力なツールとなるオープンソースのベクトルデータベースです。
主な利点をまとめると以下のようになります。
- 🧠 埋め込み(ベクトル)の扱いに特化し、セマンティック検索を容易に実現
- 💻 シンプルなAPIで学習コストが低く、迅速な開発が可能
- 🔧 インメモリ、永続化、クライアント/サーバーといった柔軟な運用モード
- 🔗 LangChainやLlamaIndexなど、主要なAIフレームワークとの高い統合性
- 🌐 オープンソース (Apache 2.0) であり、コミュニティによる開発が活発
- 🔍 メタデータフィルタリングや全文検索など、豊富な検索機能
ベクトルデータベースの活用は、LLMアプリケーションに「記憶」や「知識」を与える上で不可欠になりつつあります。Chromaはその有力な選択肢の一つであり、今後ますますその重要性を増していくと考えられます。
ぜひ、Chromaを使って、あなたのAIアプリケーション開発を加速させてみてください!
コメント