大規模エンベディングインフラ:本番AI向けベクトル生成
2025年12月11日更新
2025年12月アップデート: 10億アイテムのエンベディングコレクションは、単一のL4 GPU(2,000トークン/秒)で5.8日以上を要する。APIエンベディングコストは100万トークンあたり$0.02〜$0.18。10億個の1024次元ベクトルはインデックス作成前に約4TBのストレージを必要とする。本番RAGアプリケーションは数十億ベクトルに対するミリ秒単位の類似検索を要求する。分散GPUクラスターと積極的なキャッシングが、プロトタイプと本番システムを分ける決定的な要因となっている。
単一のNVIDIA L4 GPUは、70億パラメータのエンベディングモデルを通じて毎秒約2,000テキストトークンを処理する。この速度では、10億アイテムのコレクションのエンベディング生成に1台のマシンで5.8日以上かかる。¹ falcon-refinedwebデータセット(6,000億トークン)では9.5年以上を要することになる。大規模なエンベディングインフラには、分散システム、積極的な最適化、戦略的なキャッシングが必要であり、これらがプロトタイプRAGアプリケーションと本番対応のナレッジシステムを区別する能力となる。
エンベディングは現代のAIアプリケーションを支えている:セマンティック検索、検索拡張生成(RAG)、レコメンデーションシステム、類似性マッチングなど。しかし、組織はエンタープライズ規模でエンベディングを生成、保存、提供するために必要なインフラを一貫して過小評価している。数千のエンベディングから始まったプロトタイプは、データが数十億ベクトルに成長するにつれて、数百万ドル規模のインフラ課題に膨れ上がる可能性がある。²
エンベディングインフラの課題
スケールの次元
エンベディングインフラは3つの異なるスケーリング課題に対処する必要がある:
生成スループット: 生のテキスト、画像、その他のコンテンツをベクトル表現に変換する。数十億のドキュメントをバッチ処理するには、分散GPUクラスターと最適化されたパイプラインが必要。
ストレージ容量: 高次元ベクトルは相当なスペースを消費する。10億個の1024次元float32ベクトルは、インデックスのオーバーヘッドを除いて約4テラバイトを必要とする。
クエリレイテンシ: 本番アプリケーションは数十億ベクトルに対するミリ秒レベルの類似検索を要求し、専門的なインデックス作成とキャッシングインフラが必要となる。
コストダイナミクス
エンジニアリングチームは、エンベディングがデータベース予算を静かに食い尽くすことに気づく:³
コンピュートコスト: エンベディング生成にはGPUアクセラレーションが必要。APIベースのエンベディングは、プロバイダーとモデル品質に応じて100万トークンあたり$0.02〜$0.18のコストがかかる。
ストレージコスト: ベクトルデータベースは保存およびインデックス化されたベクトルごとに課金する。コストはデータ量に比例してスケールする—ベクトルが2倍になればストレージ費用も2倍になる。
クエリコスト: 大規模コレクションに対する類似検索には、コレクションサイズとクエリ量に応じて増加する計算リソースが必要。
1,000万ドキュメントを処理し、1日10万クエリを処理する本番RAGシステムは、エンベディング操作だけで1日$50〜$100—月額$1,500〜$3,000のコストがかかる可能性があり、これは他のインフラコストを含まない金額である。
エンベディングモデルの選択
プロバイダー比較
OpenAI text-embedding-3:⁴ - 次元数:3072(large)、1536(small) - コンテキストウィンドウ:8,192トークン - 価格:$0.13/Mトークン(large)、$0.02/Mトークン(small) - 強み:実績のある信頼性、豊富なドキュメント - 考慮事項:高次元はストレージコストを増加させる
Voyage AI voyage-3:⁵ - 次元数:1024 - コンテキストウィンドウ:32,000トークン - 価格:$0.06/Mトークン - 強み:ドメイン全体でOpenAIを平均9.74%上回る、3〜4倍小さい次元でストレージコストを削減 - 考慮事項:新しいプロバイダー、エコシステムが小さい
Cohere embed-v4: - 次元数:1024 - コンテキストウィンドウ:512トークン(限定的) - 価格:OpenAIと競争力のある価格 - 強み:優れた多言語サポート、低レイテンシ - 考慮事項:短いコンテキストウィンドウがドキュメント処理を制限
Google Gemini embedding: - 次元数:768 - コンテキストウィンドウ:2,048トークン - 価格:無料枠あり - 強み:コスト効率が良い、品質も良好 - 考慮事項:無料枠にはレート制限あり
オープンソースの代替手段
セルフホストモデルは、インフラ管理の代償としてトークンあたりのコストを排除する:⁶
E5-Large-V2: - 次元数:1024 - 性能:MTEB/BEIRベンチマークで高スコア - 最適用途:汎用検索 - インフラ:コンシューマーGPUで効率的に動作
BGE-Large: - 次元数:1024 - 性能:商用APIと競争力あり - 最適用途:コスト重視のデプロイメント - インフラ:推論が十分に最適化されている
Mistral-embed: - 次元数:1024 - 性能:ベンチマークで77.8%の精度(テスト済みモデル中最高) - 最適用途:最大の検索精度 - インフラ:より多くのGPUメモリが必要
GTE-Qwen2-7B: - 次元数:4096 - 性能:最先端の品質 - 最適用途:品質重視のアプリケーション - インフラ:A100/H100クラスのGPUが必要
選択基準
| 要素 | APIモデル | セルフホスト |
|---|---|---|
| セットアップの複雑さ | 低 | 高 |
| トークンあたりのコスト | $0.02-0.18/M | 〜$0(インフラ構築後) |
| スループット制御 | レート制限あり | 無制限 |
| データプライバシー | 外部処理 | 完全制御 |
| モデル更新 | 自動 | 手動 |
| ファインチューニング | 限定的 | 完全な柔軟性 |
APIを選択すべき場合: 月間ボリュームが1億トークン未満、チームにMLインフラの専門知識がない、コスト最適化より迅速なデプロイメントが重要な場合。
セルフホストを選択すべき場合: 月間ボリュームが1億トークンを超える、データプライバシー要件が外部処理を禁止している、ドメイン固有の語彙に対するカスタムファインチューニングが必要な場合。
バッチ処理アーキテクチャ
分散エンベディングパイプライン
大規模なエンベディング生成には、複数のGPUにわたる分散処理が必要:⁷
SkyPilotアプローチ: クラウドリージョン全体のリソースを活用することで、組織は数百のGPUに同時にアクセスできる。あるドキュメント化されたデプロイメントでは、406台のL4 GPUを使用して毎秒364,400トークンのスループットを達成し、処理時間を20時間から2.3時間に短縮した(9倍高速)。
パイプラインアーキテクチャ:
┌─────────────────┐
│ Data Source │
│ (S3/GCS/etc) │
└────────┬────────┘
│
┌────────▼────────┐
│ Coordinator │
│ (Job Scheduler)│
└────────┬────────┘
│
┌───────────────────┼───────────────────┐
│ │ │
┌────▼────┐ ┌─────▼─────┐ ┌─────▼─────┐
│ Worker 1│ │ Worker 2 │ │ Worker N │
│ (GPU) │ │ (GPU) │ │ (GPU) │
└────┬────┘ └─────┬─────┘ └─────┬─────┘
│ │ │
└───────────────────┼───────────────────┘
│
┌────────▼────────┐
│ Vector Store │
│ (Milvus/etc) │
└─────────────────┘
スループット最適化
バッチサイズチューニング:⁸ 最適なバッチサイズはシーケンス長によって大きく異なる。特定のGPUでは、最適なバッチサイズは短いシーケンスで10,000以上から、長いドキュメントで約500まで変動する。バッチサイズが不適切だと、GPU使用率が50%未満になる。
シーケンスソート: 文を長さで事前にソートすることで、バッチ内のパディングを最小化する。トークナイザーは各バッチ内の最長アイテムに合わせてシーケンスをパディングするため、類似の長さの入力をグループ化することで無駄な計算を20〜40%削減できる。
混合精度推論: FP16推論は、テンソルコアを搭載したGPUでメモリ使用量を削減し、処理を高速化する。ほとんどのエンベディング品質は、精度を下げてもほとんど劣化しない。
# 最適化されたバッチエンベディング
def embed_documents_optimized(texts, model, batch_size=64):
# パディングを最小化するために長さでソート
sorted_texts = sorted(enumerate(texts), key=lambda x: len(x[1]))
embeddings = [None] * len(texts)
for i in range(0, len(sorted_texts), batch_size):
batch = sorted_texts[i:i+batch_size]
indices, batch_texts = zip(*batch)
# GPUテンソルでエンベディングを生成
batch_embeddings = model.encode(
batch_texts,
convert_to_tensor=True, # GPU上に保持
normalize_embeddings=True
)
for idx, emb in zip(indices, batch_embeddings):
embeddings[idx] = emb
return embeddings
コスト最適化
スポットインスタンス:⁹ スポット/プリエンプティブルインスタンスを使用することで、エンベディング生成コストを61%削減できる(あるケーススタディでは$710から$277に削減)。バッチワークロードは中断に耐えられる—進捗をチェックポイントし、新しいインスタンスで再開する。
リージョンアービトラージ: GPUの可用性と価格に基づいて、クラウドリージョン全体にワークロードを分散する。SkyPilotなどのツールは、コスト最適化のためのクロスリージョンスケジューリングを自動化する。
モデル選択のトレードオフ: 小さいモデルはより低コストで高速に処理する。MiniLMはCPU上で毎秒5〜14k文を処理するのに対し、大きいモデルは1〜2k—5倍のスループット差がある。品質要件と処理コストをベンチマークして比較する。
リアルタイムエンベディングインフラ
クエリエンベディングアーキテクチャ
本番RAGシステムは、ユーザークエリに対するエンベディングをリアルタイムで生成する。レイテンシはユーザーエクスペリエンスに直接影響する:¹⁰
目標レイテンシ: - クエリエンベディング:10〜50ms - ベクトル検索:10〜100ms - 総検索時間:50〜200ms
アーキテクチャパターン:
User Query → Load Balancer → Embedding Service → Vector DB → Results
│
┌───────┴───────┐
│ GPU Pool │
│ (N replicas) │
└───────────────┘
エンベディングサービスのデプロイ
コンテナ化されたサービング: エンベディングモデルをコンテナ化されたマイクロサービスとしてデプロイする。Kubernetesがスケーリング、ロードバランシング、ヘルスチェックを処理する。
NVIDIA NIM:¹¹ NVIDIAはエンベディングモデル用の事前最適化された推論マイクロサービスを提供している。NIMコンテナは、カスタム最適化なしで本番対応のパフォーマンスを提供する。
vLLM for embeddings: LLM推論用に設計されているが、vLLMは連続バッチングやPagedAttentionなどの最適化を備えたエンベディングモデルのサービングもサポートしている。
Baseten Performance Client:¹² カスタムRustベースのクライアントは、標準のOpenAI SDK実装と比較して、バッチエンベディングワークロードで最大12倍のスループットを実現する。
レイテンシ最適化
コネクションプーリング: エンベディングサービスへの永続的な接続を維持する。接続確立はリクエストごとに10〜50msのオーバーヘッドを追加する。
リクエストバッチング: 短いウィンドウ内に到着する複数のクエリをバッチ処理する。マイクロバッチング(5〜10msウィンドウ)は、許容可能なレイテンシを維持しながらスループットを向上させる。
GPUメモリ管理: モデルをGPUメモリにロードした状態を維持する。コールドスタートはモデルロードに数秒のレイテンシを追加する。
キャッシング戦略
エンベディングキャッシングが重要な理由
エンベディング生成はリクエストごとに計算リソースを消費する。計算済みエンベディングのキャッシングは冗長な計算を排除する:¹³
節約の可能性: - 同一クエリ:100%の計算節約 - 類似クエリ(セマンティックキャッシュ):80〜95%の節約 - コーパスエンベディング:一度限りの生成コスト
キャッシングレイヤー
インメモリLRUキャッシュ:¹⁴ 頻繁にリクエストされるエンベディングへの最速アクセス。テキストコンテンツをキャッシュキーとしてハッシュ化—同一テキストはキャッシュヒットを生む。
from functools import lru_cache
import hashlib
@lru_cache(maxsize=10000)
def get_embedding_cached(text_hash: str, text: str):
return embedding_model.encode(text)
def get_embedding(text: str):
text_hash = hashlib.md5(text.encode()).hexdigest()
return get_embedding_cached(text_hash, text)
分散キャッシュ(Redis): サービスインスタンス間でキャッシュされたエンベディングを共有する。Redisは永続性を備えたサブミリ秒のアクセスを提供する。
```python import redis import numpy as np
redis_client = redis.Redis()
def get_embedding_with_cache(text: str): cache_key = f"emb:{hashlib.md5(text.encode()).hexdigest()}"
cached = redis_client.g
[翻訳用にコンテンツを切り詰め]