ベクトルデータベースとは
ベクトルデータベース(Vector Database)は、高次元ベクトルデータを効率的に保存・検索するための専用データベースです。テキスト、画像、音声などをAIモデル(Embedding Model)でベクトル化し、意味的な類似性に基づいて検索できます。
2025年現在、AIアプリケーションの85%以上がベクトルDBを利用しており、RAG(Retrieval-Augmented Generation)の基盤技術として不可欠な存在となっています。
ベクトル検索の仕組み
Embeddingとは
Embeddingは、テキストや画像などの非構造化データを、意味を保持した数値ベクトルに変換する技術です。
from openai import OpenAI
client = OpenAI()
# テキストをベクトル化
def get_embedding(text: str, model: str = "text-embedding-3-small") -> list[float]:
"""テキストを1536次元のベクトルに変換"""
response = client.embeddings.create(
input=text,
model=model
)
return response.data[0].embedding
# 例: 2つのテキストをベクトル化
vec1 = get_embedding("東京の天気は晴れです")
vec2 = get_embedding("今日の東京は快晴でした")
vec3 = get_embedding("Pythonプログラミング入門")
print(f"ベクトル次元数: {len(vec1)}") # 1536
類似度計算(Similarity Search)
ベクトル間の類似度を計算する主な手法は3つあります。
import numpy as np
def cosine_similarity(v1: list, v2: list) -> float:
"""コサイン類似度: 角度ベースの類似度(-1〜1)"""
v1, v2 = np.array(v1), np.array(v2)
return np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2))
def euclidean_distance(v1: list, v2: list) -> float:
"""ユークリッド距離: 直線距離(0〜∞、小さいほど類似)"""
v1, v2 = np.array(v1), np.array(v2)
return np.linalg.norm(v1 - v2)
def dot_product(v1: list, v2: list) -> float:
"""内積: 正規化済みベクトル向け"""
return np.dot(v1, v2)
# 類似度を計算
print(f"天気同士の類似度: {cosine_similarity(vec1, vec2):.4f}") # 0.92
print(f"天気とプログラミング: {cosine_similarity(vec1, vec3):.4f}") # 0.31
ANNアルゴリズム(近似最近傍探索)
数百万〜数十億のベクトルから高速に検索するため、**ANN(Approximate Nearest Neighbor)**アルゴリズムが使われます。
| アルゴリズム | 特徴 | 適用場面 |
|---|---|---|
| HNSW | 高精度・高速、メモリ消費大 | 精度重視のアプリ |
| IVF | メモリ効率良好、事前クラスタリング必要 | 大規模データセット |
| PQ | 圧縮率高、精度やや低下 | メモリ制約のある環境 |
| ScaNN | Google開発、バランス型 | 汎用用途 |
┌─────────────────────────────────────────────────────────────┐
│ HNSW (Hierarchical NSW) │
├─────────────────────────────────────────────────────────────┤
│ Layer 2: ○ ─────────────── ○ ─────────────── ○ │
│ │ │ │ │
│ Layer 1: ○ ─── ○ ─── ○ ─── ○ ─── ○ ─── ○ ─── ○ │
│ │ │ │ │ │ │ │ │
│ Layer 0: ○─○─○─○─○─○─○─○─○─○─○─○─○─○─○─○─○─○─○ │
│ │
│ → 上位層から下位層へ階層的にナビゲート │
└─────────────────────────────────────────────────────────────┘
主要ベクトルデータベース比較
1. Pinecone
フルマネージドのサーバーレスベクトルDB。スケーラビリティと使いやすさが強み。
from pinecone import Pinecone, ServerlessSpec
# 初期化
pc = Pinecone(api_key="YOUR_API_KEY")
# インデックス作成
pc.create_index(
name="semantic-search",
dimension=1536,
metric="cosine",
spec=ServerlessSpec(
cloud="aws",
region="us-east-1"
)
)
index = pc.Index("semantic-search")
# ベクトルをアップサート
index.upsert(
vectors=[
{
"id": "doc1",
"values": get_embedding("機械学習の基礎"),
"metadata": {"category": "AI", "source": "textbook"}
},
{
"id": "doc2",
"values": get_embedding("深層学習入門"),
"metadata": {"category": "AI", "source": "tutorial"}
}
],
namespace="articles"
)
# 類似検索
results = index.query(
vector=get_embedding("AIについて学びたい"),
top_k=5,
include_metadata=True,
namespace="articles",
filter={"category": {"$eq": "AI"}}
)
for match in results.matches:
print(f"ID: {match.id}, Score: {match.score:.4f}")
2. Weaviate
GraphQL APIとハイブリッド検索が特徴のオープンソースDB。
import weaviate
from weaviate.classes.init import Auth
from weaviate.classes.query import MetadataQuery
# クライアント初期化(Weaviate Cloud)
client = weaviate.connect_to_weaviate_cloud(
cluster_url="https://your-cluster.weaviate.network",
auth_credentials=Auth.api_key("YOUR_API_KEY"),
headers={"X-OpenAI-Api-Key": "YOUR_OPENAI_KEY"}
)
# コレクション作成
from weaviate.classes.config import Configure, Property, DataType
client.collections.create(
name="Article",
vectorizer_config=Configure.Vectorizer.text2vec_openai(),
properties=[
Property(name="title", data_type=DataType.TEXT),
Property(name="content", data_type=DataType.TEXT),
Property(name="category", data_type=DataType.TEXT)
]
)
# データ追加(自動でベクトル化)
articles = client.collections.get("Article")
articles.data.insert({
"title": "ベクトルDBの選び方",
"content": "プロダクション環境でのベクトルDB選定ポイントを解説...",
"category": "database"
})
# ハイブリッド検索(ベクトル + キーワード)
response = articles.query.hybrid(
query="ベクトルデータベース 比較",
alpha=0.5, # 0=キーワード重視, 1=ベクトル重視
limit=5,
return_metadata=MetadataQuery(score=True)
)
for obj in response.objects:
print(f"{obj.properties['title']}: {obj.metadata.score:.4f}")
client.close()
3. Qdrant
Rust製の高性能ベクトルDB。フィルタリング性能に優れる。
from qdrant_client import QdrantClient
from qdrant_client.models import (
VectorParams, Distance, PointStruct,
Filter, FieldCondition, MatchValue
)
# クライアント初期化
client = QdrantClient(
url="https://your-cluster.qdrant.io",
api_key="YOUR_API_KEY"
)
# コレクション作成
client.create_collection(
collection_name="documents",
vectors_config=VectorParams(
size=1536,
distance=Distance.COSINE
)
)
# ベクトル追加
client.upsert(
collection_name="documents",
points=[
PointStruct(
id=1,
vector=get_embedding("Rustプログラミング"),
payload={"title": "Rust入門", "lang": "ja", "year": 2025}
),
PointStruct(
id=2,
vector=get_embedding("Go言語の並行処理"),
payload={"title": "Go並行処理", "lang": "ja", "year": 2024}
)
]
)
# フィルタ付き検索
results = client.search(
collection_name="documents",
query_vector=get_embedding("システムプログラミング言語"),
query_filter=Filter(
must=[
FieldCondition(
key="year",
match=MatchValue(value=2025)
)
]
),
limit=5,
with_payload=True
)
for result in results:
print(f"{result.payload['title']}: {result.score:.4f}")
4. Milvus
大規模分散処理に強い。数十億ベクトルを扱える。
from pymilvus import (
connections, Collection, FieldSchema,
CollectionSchema, DataType, utility
)
# 接続
connections.connect(
alias="default",
host="localhost",
port="19530"
)
# スキーマ定義
fields = [
FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=True),
FieldSchema(name="title", dtype=DataType.VARCHAR, max_length=512),
FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=1536)
]
schema = CollectionSchema(fields=fields)
# コレクション作成
collection = Collection(name="articles", schema=schema)
# インデックス作成(HNSW)
index_params = {
"metric_type": "COSINE",
"index_type": "HNSW",
"params": {"M": 16, "efConstruction": 256}
}
collection.create_index(field_name="embedding", index_params=index_params)
# データ挿入
data = [
["記事タイトル1", "記事タイトル2"],
[get_embedding("内容1"), get_embedding("内容2")]
]
collection.insert(data)
collection.load()
# 検索
search_params = {"metric_type": "COSINE", "params": {"ef": 64}}
results = collection.search(
data=[get_embedding("検索クエリ")],
anns_field="embedding",
param=search_params,
limit=5,
output_fields=["title"]
)
for hits in results:
for hit in hits:
print(f"{hit.entity.get('title')}: {hit.distance:.4f}")
5. Chroma
ローカル開発に最適な軽量DB。プロトタイピングに便利。
import chromadb
from chromadb.utils import embedding_functions
# クライアント初期化
client = chromadb.PersistentClient(path="./chroma_db")
# OpenAI Embeddingを使用
openai_ef = embedding_functions.OpenAIEmbeddingFunction(
api_key="YOUR_OPENAI_KEY",
model_name="text-embedding-3-small"
)
# コレクション作成
collection = client.get_or_create_collection(
name="my_documents",
embedding_function=openai_ef,
metadata={"hnsw:space": "cosine"}
)
# ドキュメント追加(自動でベクトル化)
collection.add(
documents=[
"Pythonは初心者に優しいプログラミング言語です",
"JavaScriptはWeb開発に欠かせない言語です",
"Rustはメモリ安全性を重視したシステム言語です"
],
metadatas=[
{"lang": "Python", "level": "beginner"},
{"lang": "JavaScript", "level": "intermediate"},
{"lang": "Rust", "level": "advanced"}
],
ids=["doc1", "doc2", "doc3"]
)
# 検索
results = collection.query(
query_texts=["プログラミング初心者向けの言語"],
n_results=2,
where={"level": "beginner"}
)
print(results["documents"])
主要サービス比較表
| 機能 | Pinecone | Weaviate | Qdrant | Milvus | Chroma |
|---|---|---|---|---|---|
| ホスティング | マネージド | 両対応 | 両対応 | 両対応 | ローカル中心 |
| 最大ベクトル数 | 無制限 | 数十億 | 数十億 | 数百億 | 数百万 |
| アルゴリズム | 独自 | HNSW | HNSW | 複数対応 | HNSW |
| フィルタリング | ○ | ◎ | ◎ | ○ | ○ |
| ハイブリッド検索 | △ | ◎ | ○ | ○ | △ |
| マルチテナント | ◎ | ○ | ◎ | ○ | △ |
| 日本語対応 | ○ | ○ | ○ | ○ | ○ |
| 無料枠 | あり | あり | あり | OSS | OSS |
| 価格(月額) | $70〜 | $25〜 | $25〜 | OSS/有料 | 無料 |
既存DBのベクトル拡張
pgvector(PostgreSQL)
既存のPostgreSQLにベクトル検索を追加できます。
-- pgvector拡張を有効化
CREATE EXTENSION IF NOT EXISTS vector;
-- テーブル作成
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
title TEXT NOT NULL,
content TEXT,
embedding VECTOR(1536),
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- HNSWインデックス作成(高速検索用)
CREATE INDEX ON documents
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
-- ベクトル検索関数
CREATE OR REPLACE FUNCTION search_documents(
query_embedding VECTOR(1536),
match_threshold FLOAT DEFAULT 0.7,
match_count INT DEFAULT 10
)
RETURNS TABLE (
id INT,
title TEXT,
content TEXT,
similarity FLOAT
)
LANGUAGE plpgsql AS $$
BEGIN
RETURN QUERY
SELECT
d.id,
d.title,
d.content,
1 - (d.embedding <=> query_embedding) AS similarity
FROM documents d
WHERE 1 - (d.embedding <=> query_embedding) > match_threshold
ORDER BY d.embedding <=> query_embedding
LIMIT match_count;
END;
$$;
import psycopg2
from pgvector.psycopg2 import register_vector
# 接続
conn = psycopg2.connect("postgresql://user:pass@localhost/mydb")
register_vector(conn)
cur = conn.cursor()
# ベクトル挿入
embedding = get_embedding("サンプルテキスト")
cur.execute(
"INSERT INTO documents (title, content, embedding) VALUES (%s, %s, %s)",
("タイトル", "内容", embedding)
)
# ベクトル検索
query_embedding = get_embedding("検索クエリ")
cur.execute(
"SELECT * FROM search_documents(%s, 0.7, 5)",
(query_embedding,)
)
results = cur.fetchall()
Elasticsearch + kNN
PUT /articles
{
"mappings": {
"properties": {
"title": { "type": "text" },
"content": { "type": "text" },
"embedding": {
"type": "dense_vector",
"dims": 1536,
"index": true,
"similarity": "cosine"
}
}
}
}
from elasticsearch import Elasticsearch
es = Elasticsearch("https://localhost:9200")
# kNN検索
response = es.search(
index="articles",
knn={
"field": "embedding",
"query_vector": get_embedding("検索クエリ"),
"k": 10,
"num_candidates": 100
}
)
RAGでの活用パターン
基本的なRAGパイプライン
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_qdrant import QdrantVectorStore
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
# ベクトルストア設定
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = QdrantVectorStore.from_existing_collection(
embedding=embeddings,
collection_name="knowledge_base",
url="http://localhost:6333"
)
# カスタムプロンプト
prompt_template = """以下のコンテキストを使用して質問に回答してください。
コンテキストに情報がない場合は「情報が見つかりません」と回答してください。
コンテキスト:
{context}
質問: {question}
回答:"""
prompt = PromptTemplate(
template=prompt_template,
input_variables=["context", "question"]
)
# RAGチェーン構築
qa_chain = RetrievalQA.from_chain_type(
llm=ChatOpenAI(model="gpt-4o", temperature=0),
chain_type="stuff",
retriever=vectorstore.as_retriever(
search_type="mmr", # Maximum Marginal Relevance
search_kwargs={"k": 5, "fetch_k": 20}
),
chain_type_kwargs={"prompt": prompt},
return_source_documents=True
)
# 質問応答
result = qa_chain.invoke({"query": "ベクトルDBの選び方は?"})
print(result["result"])
print("参照元:", [doc.metadata for doc in result["source_documents"]])
Rerankerによる精度向上
from langchain.retrievers import ContextualCompressionRetriever
from langchain_cohere import CohereRerank
# Cohere Rerankを使用
reranker = CohereRerank(
cohere_api_key="YOUR_COHERE_KEY",
top_n=5,
model="rerank-multilingual-v3.0"
)
# 圧縮リトリーバー
compression_retriever = ContextualCompressionRetriever(
base_compressor=reranker,
base_retriever=vectorstore.as_retriever(search_kwargs={"k": 20})
)
# 検索(20件取得 → Rerankで5件に絞り込み)
docs = compression_retriever.invoke("ベクトルDBの比較")
マルチモーダルRAG
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
import base64
def encode_image(image_path: str) -> str:
with open(image_path, "rb") as f:
return base64.standard_b64encode(f.read()).decode("utf-8")
# 画像とテキストを組み合わせた検索
llm = ChatOpenAI(model="gpt-4o")
# 画像からテキスト説明を生成
image_data = encode_image("diagram.png")
response = llm.invoke([
HumanMessage(content=[
{"type": "text", "text": "この図を説明してください"},
{
"type": "image_url",
"image_url": {"url": f"data:image/png;base64,{image_data}"}
}
])
])
# 生成された説明をベクトル化して保存
description = response.content
embedding = get_embedding(description)
# vectorstore.add(...)
パフォーマンス比較
ベンチマーク結果(100万ベクトル、1536次元)
| 指標 | Pinecone | Qdrant | Milvus | pgvector |
|---|---|---|---|---|
| 挿入速度 | 15K/秒 | 25K/秒 | 30K/秒 | 5K/秒 |
| 検索レイテンシ(p99) | 15ms | 8ms | 12ms | 45ms |
| Recall@10 | 98.5% | 99.2% | 98.8% | 97.5% |
| メモリ効率 | 高 | 高 | 中 | 中 |
コスト比較(月間100万クエリ、1000万ベクトル)
| サービス | 月額コスト | 備考 |
|---|---|---|
| Pinecone Serverless | $150〜300 | 使用量に応じた課金 |
| Weaviate Cloud | $100〜200 | インスタンスサイズ依存 |
| Qdrant Cloud | $100〜250 | RAM/ストレージ課金 |
| Milvus(セルフホスト) | $200〜500 | インフラ費用 |
| pgvector(RDS) | $150〜400 | DBインスタンス費用 |
選定ガイドライン
┌─────────────────────────────────────────────────────────────┐
│ ベクトルDB選定フローチャート │
├─────────────────────────────────────────────────────────────┤
│ │
│ プロトタイプ/PoC? ─── Yes ──→ Chroma │
│ │ │
│ No │
│ ↓ │
│ 既存PostgreSQL活用? ─── Yes ──→ pgvector │
│ │ │
│ No │
│ ↓ │
│ フルマネージド希望? ─── Yes ──→ Pinecone │
│ │ │
│ No │
│ ↓ │
│ 10億ベクトル以上? ─── Yes ──→ Milvus │
│ │ │
│ No │
│ ↓ │
│ フィルタ/ハイブリッド検索重視? ─── Yes ──→ Qdrant/Weaviate │
│ │ │
│ No │
│ ↓ │
│ ──→ Qdrant(汎用性が高い) │
│ │
└─────────────────────────────────────────────────────────────┘
ベストプラクティス
1. 適切なEmbeddingモデル選択
# 用途別推奨モデル
EMBEDDING_MODELS = {
"general": "text-embedding-3-small", # コスト効率
"high_accuracy": "text-embedding-3-large", # 高精度
"multilingual": "multilingual-e5-large", # 多言語対応
"japanese": "cl-tohoku/bert-base-japanese" # 日本語特化
}
2. チャンキング戦略
from langchain.text_splitter import RecursiveCharacterTextSplitter
# 日本語対応チャンキング
splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
separators=["\n\n", "\n", "。", "、", " ", ""]
)
chunks = splitter.split_text(long_document)
3. メタデータの活用
# フィルタリング可能なメタデータ設計
metadata = {
"source": "company_docs",
"category": "engineering",
"date": "2025-01-12",
"author": "team_lead",
"access_level": "internal",
"version": 2
}
まとめ
2025年のベクトルデータベースは、AI/LLMアプリケーションの中核インフラとして成熟しています。用途に応じた選定が重要です。
- スタートアップ/PoC: Chroma、pgvector
- 本番環境(マネージド): Pinecone、Weaviate Cloud
- 本番環境(セルフホスト): Qdrant、Milvus
- 既存システム統合: pgvector、Elasticsearch
RAGの品質はベクトルDBの選択と設定に大きく依存します。適切なインデックス設計、チャンキング戦略、Rerankerの活用で、検索精度を最大化しましょう。
← 一覧に戻る