
Ollamaで埋め込みモデルをローカル実行:コールドスタートとウォームGPUの計測結果
Ollamaを使えば、埋め込みモデルをローカルで実行でき、インデックス化するチャンクごとにOpenAIへ支払っていた100万トークンあたり0.02ドルのコストをゼロにできます。その代償として、GPUの所有、コールドスタートへの対応、そして運用負担を引き受けることになります。OllamaはAPIキー不要でポート11434番を通じてこれらのモデルを提供します。以下では、ollama pullからクエリに回答するウォーム状態のベクトル検索に至るまでの完全なワークフローを紹介します。
主なポイント
- Ollamaは、APIキー不要・トークン単価$0で、
http://localhost:11434上のPOST /api/embed経由でローカルに埋め込みを提供します。 /api/embed(現行、バッチ配列対応)を使用してください。/api/embeddingsはレガシーであり、404エラーの主な原因です。- 人気のローカルモデル:
nomic-embed-text(768次元)、mxbai-embed-large(1024)、bge-m3(1024)、embeddinggemma(768)。 - ベクトルDBのカラムと埋め込み次元を一致させ、
keep_aliveでモデルを固定してコールドスタートの遅延を回避しましょう。
Ollamaで埋め込みをローカル実行するために必要なものは?
埋め込みをローカルで実行するために必要なのは、埋め込みモデル、ポート11434番で動作するOllamaサーバー、そして出力を保持するベクトルストアの3つだけです。Ollamaがモデルをダウンロードして提供し、あなたのコードがテキストを/api/embedにPOST送信します。生成されたベクトルはpgvector、Qdrant、Chromaなどのデータベースに格納されます。クラウドとの往復通信もなく、トークンごとの課金もありません。
わずか2つのコマンドで、1分以内に動作する埋め込み環境が構築できます。
ollama pull nomic-embed-text
curl http://localhost:11434/api/embed -d '{
"model": "nomic-embed-text",
"input": "The quick brown fox"
}'これがクイックスタートのすべてです。本チュートリアルの残りの部分では、モデルの選択、ストアの設定、そして誰もが直面する2つの落とし穴(エンドポイントの混同とコールドスタートのペナルティ)について詳しく解説します。
ステップ1:Ollamaをインストールし、埋め込みモデルをPullする
Ollamaをインストールし、サーバーがポート11434番でリッスンしていることを確認したら、埋め込みモデルをPullします。Ollamaはバックグラウンドサービスとして動作するため、ollama pull nomic-embed-textを実行すると重みがダウンロードされ、次の/api/embed呼び出しで提供可能になります。埋め込みモデルはチャットモデルと比較して非常に小さいため、この処理は高速です。
# macOS / Linux install
curl -fsSL https://ollama.com/install.sh | sh
# Make sure the server is up (background service on :11434)
ollama serve # only if it isn't already running
# Pull an embedding model and health-check the server
ollama pull nomic-embed-text
curl http://localhost:11434 # should return "Ollama is running"ここで注目すべき点は、nomic-embed-textのような埋め込みモデルのパラメータ数が1億3700万程度で、ダウンロードサイズは約274 MBであることです。これに対し、チャットモデルは数GB規模になります。VRAMへのロードは約1秒で完了します。もしチャットモデル用の完全なローカルLLM環境を埋め込みモデルと併せて構築したい場合は、ローカルLLM向けにOllamaを設定するガイドをご覧ください。また、curlコマンドではなくクリック操作で管理したい場合は、ローカルOllamaモデル用のUIも参照してください。
プロのヒント:リクエストを送信する前にサーバーが起動している必要があります。:11434で接続拒否が発生する場合、ほぼ確実にollama serveが起動していないことが原因です。
どのローカル埋め込みモデルをPullすべきか?
大多数のローカルRAG用途では、768次元のnomic-embed-textが安全なデフォルト選択肢です。これはOpenAIの旧ada-002を上回る性能を持ち、ほぼあらゆる環境で動作します。多言語対応や長文コンテキストの検索が必要な場合はbge-m3やqwen3-embedding、小規模ハードウェアで速度を重視する場合はall-minilm、新しいGoogle製オプションとしてはembeddinggemmaを選択します。以下の表は、品質ランキングではなく、サービング判断のための現在のOllama埋め込みモデルライブラリを対象としています。
| モデル(正確なタグ) | パラメータ数 | 出力次元 | コンテキスト | 備考 |
|---|---|---|---|---|
| nomic-embed-text | 1億3700万 | 768 | デフォルト2048(ネイティブ8192、num_ctxで増加可能) | 最も人気のあるローカル埋め込みモデル;ada-002を上回る |
| embeddinggemma | 3億 | 768(MRL 512/256/128) | 約2K | Google製;現在Ollama推奨モデル |
| mxbai-embed-large | 3億3500万 | 1024 | 512 | mixedbread.ai製;より大規模なモデルと同等の性能 |
| bge-m3 | 5億6700万 | 1024 | 8192 | BAAI製;密、疎、マルチベクトル、多言語対応 |
| snowflake-arctic-embed | 2200万~3億3500万 | 最大1024 | 512 | Snowflake製;サイズ範囲あり |
| granite-embedding | 3000万 / 2億7800万 | 384 / 768 | 512 | IBM製;TinyとSmall |
| qwen3-embedding | 0.6B/4B/8B | 1024/2560/4096(ユーザー定義可能) | 32K | オープンソース中最強の多言語・コードRAG向け |
| all-minilm | 2200万 / 3300万 | 384 | 256 | 最速かつ最小 |
「best ollama embedding model reddit」などのスレッドでは、一般的なRAGにはnomic-embed-text、多言語対応にはbge-m3というコンセンサスが繰り返し見られ、これは私たちが提供している構成とも一致します。スコア付きのプロバイダ横断ランキングが必要な場合は、ハブの仕事です:RAG用にどの埋め込みモデルを選ぶべきか。ここでは意図的にMTEB数値を省略しています。リーダーボードだけでは誤解を招く理由については、併読記事RAGにおけるMTEBスコアの仕組みで解説しています。
ステップ2:/api/embed経由で埋め込みを生成する
テキストをPOST /api/embedに送信すると、OllamaはL2正規化されたベクトルを返します。つまり、各ベクトルは単位長さであり、コサイン類似度を直接計算できます。Ollama埋め込みドキュメントによると、現行のエンドポイントは単一文字列またはバッチ用の配列を受け取るinputフィールドを取り、{"embeddings": [[...]]}を返します。
生HTTP呼び出しは以下の通りです:
curl http://localhost:11434/api/embed -d '{
"model": "nomic-embed-text",
"input": ["first chunk", "second chunk", "third chunk"]
}'Pythonでは、公式クライアントを使用すればバッチごとに1行で済みます:
import ollama
resp = ollama.embed(
model="nomic-embed-text",
input=["first chunk", "second chunk", "third chunk"],
options={"num_ctx": 8192}, # raise context for long chunks
)
vectors = resp["embeddings"] # list of 768-float lists, L2-normalizedinput配列を通じたバッチ処理は、スループット向上の主要な杠杆です。64チャンクを含む1回のリクエストは、64回の個別リクエストよりも大幅に優れています。コールごとのオーバーヘッドを1回だけ支払うためです。num_ctxの増加にも注意してください:nomic-embed-textはネイティブで8192トークンをサポートしていますが、デフォルトのウィンドウは2048トークンです。そのため、これを増やさない限り、長いチャンクはサイレントに切り捨てられます。埋め込みは、これによって供給される完全なRAGパイプラインの一部であり、チャンキングや検索ロジックはここではなくそこで行われます。
/api/embed vs /api/embeddings vs /v1/embeddings:違いは何?
/api/embedが現行のエンドポイントです。/api/embeddingsは、「Ollama embeddings not working」といった投稿の背後にある非推奨のエンドポイントです。レガシルートは単数のpromptフィールドを使用し、embedding(sなし)を返しますが、現行ルートはinputを使用し、バッチを受け付け、embeddingsを返します。3つ目のルート/v1/embeddingsはOpenAI互換で、dimensionsパラメータを受け付けます。
| エンドポイント | ステータス | 入力フィールド | 応答フィールド | バッチ入力? | dimensionsパラメータ? |
|---|---|---|---|---|---|
| /api/embed | 現行 | input(文字列または配列) | embeddings | はい | いいえ |
| /api/embeddings | レガシー/非推奨 | prompt(単一) | embedding | いいえ | いいえ |
| /v1/embeddings | OpenAI互換 | input | data[].embedding | はい | はい(マトリョーシカ) |
404エラーや奇妙な応答形状が発生した場合、おそらく/api/embeddings(レガシー)を使用しています。/api/embedに切り替え、embeddingではなくembeddingsキーを読み取ってください。この1文字の違いが、古いチュートリアルをコピーする多くの人々を悩ませています。
/v1/embeddingsルートが重要なのは、特定のケース、つまりOpenAIからの移行時です。dimensionsパラメータを受け付けるため、マトリョーシカ対応モデルをターゲットサイズまで切り詰めることができ、次に説明する1536次元の不一致問題に対する解決策となります。
ステップ3:ベクトルの保存と検索(pgvector、Qdrant、またはChroma)
768浮動小数点ベクトルを最近傍検索可能なデータベースに保存し、コサイン距離でクエリを実行します。私たちのRAG構築では、すでにPostgresを使用しているチームのために、**Postgres plus pgvector**をデフォルトとしています。これにより、埋め込みデータをリレーショナルデータと隣接して保持できるためです。拡張機能を有効にし、モデルの次元に一致するVECTOR(768)カラムを宣言し、挿入して、<=>コサイン演算子でクエリを実行します。
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE chunks (
id bigserial PRIMARY KEY,
body text,
embedding vector(768) -- must match nomic-embed-text
);
-- Insert a row (embedding comes from ollama.embed)
INSERT INTO chunks (body, embedding) VALUES ('first chunk', '[0.01, -0.02, ...]');
-- Top-5 nearest chunks by cosine distance
SELECT body, 1 - (embedding <=> '[0.01, -0.02, ...]') AS score
FROM chunks
ORDER BY embedding <=> '[0.01, -0.02, ...]'
LIMIT 5;QdrantとChromaも概念的には同じように動作します。モデルに一致する固定ベクトルサイズでコレクションを作成し、アップサートおよび検索を行います。ルールはどこでも共通です:ベクトルデータベースの選択は、次元を正しく合わせるほど重要ではありません。Qdrant、Chroma、pgvectorはいずれも、サイズがコレクションと一致しないベクトルを拒否するためです。まだ迷っている場合は、Qdrant vs Chroma vs pgvector比較をご覧ください。
移行時の落とし穴:Ollamaモデルにはネイティブで1536次元のものがないため、既存のVECTOR(1536) pgvectorカラムはこれらを拒否します。解決策は3つあります。(1) カラムの次元に一致するモデルを選ぶ、(2) qwen3-embeddingやembeddinggemmaのようなマトリョーシカモデルで/v1/embeddingsをdimensionsパラメータと共に使用し、1536に切り詰める、(3) カラムをモデルのネイティブ次元(例:VECTOR(768))で再宣言する、です。
RTX 4090でnomic-embed-textを計測:コールドスタート vs ウォームGPU
実際に計測しました。私たちの環境(Ubuntu 22.04、RTX 4090 24 GB、Ollama 0.5.x、768次元のnomic-embed-text)では、アイドル状態後の最初の/api/embedは、重みがVRAMにロードされる間、約1.3秒かかりました。ウォーム状態になると、埋め込みごとにp50が約9 ms、p95が約22 msとなりました。64のバッチ処理では、約600埋め込み/秒を維持しました。
| 指標 | コールド(アイドル後の初回リクエスト) | ウォーム(定常状態) |
|---|---|---|
| 待機時間 p50 | 約1.3秒 | 約9 ms |
| 待機時間 p95 | 約1.3秒 | 約22 ms |
| スループット(batch=64) | 該当なし | 約600埋め込み/秒 |
| 10,000チャンクのコーパス | 該当なし | 約50秒 |
ここで、「なぜOllamaの埋め込みは遅かったりタイムアウトするのか」という疑問に答える落とし穴があります。デフォルトでは、Ollamaは約5分間アイドル状態が続くとVRAMからモデルをアンロードします。そのため、次のリクエストで再び約1.3秒のコールドスタートを支払うことになり、本番環境ではランダムなスパイクのように感じられます。解決策はkeep_aliveです:
curl http://localhost:11434/api/embed -d '{
"model": "nomic-embed-text",
"input": "keep me warm",
"keep_alive": -1
}'keep_alive: -1を設定すると、モデルがVRAMに無期限に固定され、すべてのリクエストがウォームパスで処理されます。ウォーム状態のRTX 4090上のnomic-embed-textはp95を約22 msで維持しました。しかし、5分間アイドルにすると、次のリクエストで再び約1.3秒のコールドスタートを支払います。待機時間に敏感なサービスであれば、固定しましょう。
埋め込みのセルフホスティングは価値があるか? コスト vs API
ローカル埋め込みのコストは、限界費用においてトークン100万個あたり約$0(電気代のみ)です。対照的に、OpenAI text-embedding-3-smallは約$0.02です。しかし、正直な答えは:セルフホスティングが勝つのは、トークン量が閾値を超えた場合のみです。月間数億トークン未満の場合、節約されるドルではなく、運用時間とアイドルGPUのコストを支払っていることになります。少量の場合はAPIの利便性が勝ります。
| 要因 | ローカルOllama | OpenAI API |
|---|---|---|
| 100万トークンあたりの限界費用 | 約$0(電気代のみ) | 約$0.02 |
| 初期費用 | GPU + セットアップ | $0 |
| データプライバシー | マシン外に出ない | プロバイダに送信 |
| 運用負担 | サーバーを自分で実行 | なし |
| 適している場合 | 高ボリューム、プライベートデータ | 低ボリューム、GPUなし |
埋め込みのセルフホスティングがAPIを上回るのは、月間おおよそ数億トークン以上の場合のみです。それ以下では、節約されるドルではなく運用時間で支払っています。ローカルが不適切な場合:クエリ量が少ない、GPUがない、またはサーバーを健全に保つ運用キャパシティがないチームです。これらの場合、管理されたAPIが現実的な選択であり、Voyage、OpenAI、Cohereの埋め込みAPI比較を読むのが次のステップです。GPUと運用を所有することに躊躇していますか? プライバシーのために埋め込みをローカルに保ちつつ、セットアップと翌日以降のメンテナンス支援を導入するチームも多く、これは私たちのAI統合サービスが扱う種類の構築です。ランタイムを比較したい場合は、モデルをローカルで実行するための他のツールをご覧ください。
著者について
Mert Batur GurbuzはTechsy.ioの共同創設者であり、同社チームはB2B顧客向けにAIエージェント、自動化システム、音声/SDRパイプラインを提供しています。バーミンガム大学で学び、Techsyチームが生産環境で実際に使用しているLLMツールスタックについて執筆しています。
資格:共同創設者、Techsy.io、バーミンガム大学。LinkedInでつながる。
よくある質問
Ollamaで埋め込みをローカル実行することは、実際にOpenAI APIより安いですか?
トークン量が閾値を超えた場合のみです。ローカルの限界費用はトークン100万個あたり約$0(電気代含む)で、OpenAI text-embedding-3-smallは約$0.02です。月間数億トークン未満の場合、利便性と運用ゼロの点でAPIが勝ります。セルフホスティングのもう一つの理由はプライバシーです。データがマシン外に出ることはありません。
/api/embedと/api/embeddingsの違いは何ですか?
/api/embedが現行のエンドポイントです。inputフィールド(文字列またはバッチ用配列)を取り、embeddingsを返します。/api/embeddingsは、単数のpromptフィールドを持ちembeddingを返す、レガシーで非推奨のルートです。404エラーや予期しない応答形状が発生する場合、ほぼ確実に古い方を使用しています。
Ollamaの埋め込みは無料ですか?
はい、トークンごとの課金やAPIキーが存在しないという意味では無料です。ハードウェアと実行のための電気代を支払います。クラウドAPIのような従量課金はないため、GPUが稼働していれば、追加で100万埋め込みを生成しても限界費用は実質ゼロです。
RAG向けのデフォルトまたは最適なOllama埋め込みモデルは何ですか?
768次元のnomic-embed-textが、ローカルRAG向けの人気デフォルトです。OpenAIの旧ada-002を上回り、 modestなハードウェアで動作します。多言語や長文コンテキストの作業には、bge-m3やqwen3-embeddingがより強力です。プロバイダ横断のランキング付き比較については、埋め込みモデルハブをご覧ください。
なぜOllamaの埋め込みは遅かったりタイムアウトしたりするのですか?
アイドル後の初回リクエストは、モデルがVRAMにロードされる間、コールドスタートを支払います。私たちのRTX 4090では約1.3秒です。Ollamaはデフォルトで約5分間アイドル状態が続くとモデルをアンロードするため、间歇的な遅延は通常、繰り返されるコールドスタートが原因です。モデルをVRAMに固定するにはkeep_alive: -1を設定してください。
OllamaはOpenAIの1536次元埋め込みと一致できますか?
Ollamaモデルにはネイティブで1536次元のものがないため、既存のVECTOR(1536)カラムへの移行は次元不一致で失敗します。qwen3-embeddingやembeddinggemmaのようなマトリョーシカモデルでdimensionsパラメータ付きの/v1/embeddingsを呼び出して1536に切り詰めるか、カラムをモデルのネイティブサイズ(例:VECTOR(768))で再宣言することで修正できます。
埋め込みモデルをローカルで実行するためにGPUは必要ですか?
いいえ。nomic-embed-text(1億3700万)やall-minilm(2200万)のような小規模モデルは、少量の場合CPUでも問題なく動作します。GPUは埋め込みごとの待機時間をシングル-digitミリ秒に短縮し、バッチスループットを毎秒数百埋め込みまで引き上げます。これは、数千のチャンクを一度にインデックス化する際に重要です。
PythonやLangChainでOllama埋め込みを使用するにはどうすればよいですか?
公式クライアント呼び出しはollama.embed(model="nomic-embed-text", input=["chunk a", "chunk b"])で、embeddingsリストを返します。LangChainでは、http://localhost:11434を指すOllamaEmbeddingsクラスを使用し、他の埋め込みプロバイダと同様にベクトルストアのfrom_documentsまたはadd_textsメソッドに渡します。
Ollama埋め込みモデルが扱えるコンテキスト長是多少ですか?
モデルによって異なります。nomic-embed-textはネイティブで8192トークンをサポートしますが、提供時にはデフォルトで2048トークンのウィンドウとなるため、長いチャンクの場合num_ctxを8192に増やさないとサイレントに切り捨てられます。bge-m3は8192を扱い、qwen3-embeddingは最大32Kまで可能です。all-minilmは256トークンに制限されています。