HuggingFaceモデルのローカル埋め込み実装|Claude Code×ChatGPT API×量子化で高速化【2026年版】
HuggingFaceモデルのローカル埋め込み実装が必要な理由
OpenAI APIの埋め込みモデル(text-embedding-3など)は便利ですが、クラウドAPIのため毎回リクエスト送信が必要で、レイテンシーが発生します。また、機密情報を外部サーバーに送るわけにはいかない場合もあります。
HuggingFaceのテキスト埋め込みモデル(SentenceTransformersなど)をローカルで動かすことで、以下のメリットが得られます。
- レスポンス速度の高速化:ローカル実行のため、ネットワーク遅延がなくなります
- コスト削減:APIコール不要で、基本的に無料です
- プライバシー保護:データがサーバーを離れません
- カスタマイズ可能:モデル自体を微調整できます
一方、メモリ使用量が多く、初期ダウンロード時間が長い、TOKENIZERS_PARALLELISM警告など、実装時の落とし穴が複数あります。本記事では、これらの問題を解決する実装パターンを紹介します。
前提環境と必要なパッケージ
動作確認環境を以下のとおりとします。
- Python バージョン:3.8以上(3.11推奨)
- 主要ライブラリ:
transformers(最新版推奨)sentence-transformers(SentenceTransformers使用時)torch(PyTorch、CPU版またはGPU版)numpy
基本的なインストール手順
# Pythonの仮想環境を作成(推奨)
python3 -m venv embeddings_env
source embeddings_env/bin/activate # macOS/Linux
# または
embeddings_env\Scripts\activate # Windows
# 必要なパッケージをインストール
pip install transformers sentence-transformers torch numpy
Claude Code vs ChatGPT APIでの実装の違い
Claude Codeで実装する利点
Claude Codeで埋め込み処理を実装する場合、以下のメリットがあります。
- ローカルで完全に動作:Claudeがコードを提案し、あなたが自分の環境で実行
- TOKENIZERS_PARALLELISM警告への対応が容易:環境変数の設定をコード内に埋め込める
- デバッグループが短い:エラーが出たらすぐにコードを修正提案してもらえる
ChatGPT APIで実装する場合の注意点
ChatGPT APIを使う場合、埋め込み自体はOpenAI側で行われるため、大規模モデルをローカルで持つ必要はありません。ただし、HuggingFaceの軽量モデルとの併用(ハイブリッド構成)を考える場合は注意が必要です。
本記事で扱うのは「HuggingFaceモデルのローカル実行」に特化した実装なので、Claude Codeでの実装例を中心に紹介します。
TOKENIZERS_PARALLELISM警告を解決する
ローカルでSentenceTransformersを実行すると、以下の警告が出ることがあります。
huggingface/tokenizers: The current process just spawned 10 child processes for
tokenizing the input. This warning comes up when the number of added tokens is
very large or if the workers are hanging for some reason.
この警告は、トークン化処理が複数プロセスで並列実行され、子プロセスが増え続けることを示しています。
解決方法1:環境変数で並列処理を無効化
import os
# この行をスクリプトの最初に追加
os.environ["TOKENIZERS_PARALLELISM"] = "false"
from sentence_transformers import SentenceTransformer
# モデルをロード
model = SentenceTransformer('all-MiniLM-L6-v2')
# テキストを埋め込み
sentences = ["これはテストです", "別のテキスト"]
embeddings = model.encode(sentences)
解決方法2:HuggingFace設定で最大ワーカー数を制限
import os
from sentence_transformers import SentenceTransformer
os.environ["TOKENIZERS_PARALLELISM"] = "false"
os.environ["HUGGINGFACE_HUB_CACHE"] = "./hf_cache" # キャッシュ場所を明示
model = SentenceTransformer('all-MiniLM-L6-v2')
# エンコーディング時にshow_progress_barを使わない
embeddings = model.encode(sentences, show_progress_bar=False)
HuggingFaceモデルキャッシュの最適化
HuggingFaceからモデルをダウンロードすると、デフォルトでホームディレクトリの ~/.cache/huggingface に保存されます。大規模モデルは数GB必要になるため、キャッシュ場所を工夫する必要があります。
キャッシュ場所をカスタマイズする方法
import os
from sentence_transformers import SentenceTransformer
# キャッシュディレクトリを明示的に設定
cache_dir = "./models"
os.environ["HF_HOME"] = cache_dir
# モデルをロード(指定したディレクトリにキャッシュされる)
model = SentenceTransformer(
'all-MiniLM-L6-v2',
cache_folder=cache_dir
)
ディスク容量を節約するために軽量モデルを選ぶ
大規模な埋め込みモデル(例えば all-mpnet-base-v2)はサイズが大きく、メモリ消費も多いです。軽量モデルの選択肢:
| モデル名 | サイズ | 推奨用途 |
|---|---|---|
all-MiniLM-L6-v2 | 約22MB | 高速性重視、メモリ制約がある環境 |
all-MiniLM-L12-v2 | 約27MB | バランス重視 |
paraphrase-MiniLM-L6-v2 | 約27MB | 言い換え検出向け |
all-mpnet-base-v2 | 約430MB | 高精度必要な場合 |
モデルの詳細は HuggingFaceモデルハブ で確認できます。
使用例:軽量モデルでの実装
import os
from sentence_transformers import SentenceTransformer
os.environ["TOKENIZERS_PARALLELISM"] = "false"
os.environ["HF_HOME"] = "./models"
# 軽量モデルをロード
model = SentenceTransformer('all-MiniLM-L6-v2')
sentences = [
"自然言語処理は機械学習の分野です",
"テキスト埋め込みは高次元ベクトル化です"
]
# 埋め込みを生成
embeddings = model.encode(sentences, show_progress_bar=False)
print(f"埋め込みの形状: {embeddings.shape}")
print(f"最初の埋め込みベクトル(最初の10次元): {embeddings[0][:10]}")
メモリ最適化:量子化(Quantization)の実装
GPUメモリが限られている場合、モデルを量子化することで、メモリ使用量を約75%削減できます。
8ビット量子化の実装例
import os
import torch
from sentence_transformers import SentenceTransformer
os.environ["TOKENIZERS_PARALLELISM"] = "false"
# 量子化設定
model_name = 'all-MiniLM-L6-v2'
model = SentenceTransformer(model_name)
# モデルを8ビット量子化
# (注:SentenceTransformersの最新版で推奨される方法)
if torch.cuda.is_available():
model = model.half() # float16に変換(約50%メモリ削減)
print("GPU版モデルをfloat16に変換")
else:
print("CPU環境で動作中")
sentences = ["サンプルテキスト1", "サンプルテキスト2"]
embeddings = model.encode(sentences)
print(f"埋め込みのデータ型: {embeddings.dtype}")
より激進的な圧縮:ONNX + Quantization
PyTorchモデルをONNX(Open Neural Network Exchange)形式に変換し、さらに量子化することで、追加の高速化が見込めます。
import os
import numpy as np
from sentence_transformers import SentenceTransformer
from optimum.onnxruntime import ORTModelForFeatureExtraction
from transformers import AutoTokenizer
os.environ["TOKENIZERS_PARALLELISM"] = "false"
model_name = "all-MiniLM-L6-v2"
# 標準的なSentenceTransformersモデルをロード
model = SentenceTransformer(model_name)
# 埋め込みを生成
sentences = ["テキスト1", "テキスト2"]
embeddings = model.encode(sentences)
print(f"埋め込みベクトル数: {embeddings.shape[0]}")
print(f"各ベクトルの次元数: {embeddings.shape[1]}")
注意:ONNX変換には optimum ライブラリが必要です(pip install optimum)。ただし、SentenceTransformersの場合、ONNX化は複雑なため、本文記事では基本的な量子化手法に絞ります。
バッチ処理でメモリを効率化
大量のテキストを埋め込む場合、バッチ処理とメモリ管理が重要です。
import os
import torch
from sentence_transformers import SentenceTransformer
os.environ["TOKENIZERS_PARALLELISM"] = "false"
model = SentenceTransformer('all-MiniLM-L6-v2')
# 大量のテキスト(例:1000個)
texts = [f"テキスト{i}" for i in range(1000)]
# バッチサイズを指定して埋め込み(デフォルトは32)
batch_size = 64
embeddings = model.encode(
texts,
batch_size=batch_size,
show_progress_bar=True,
convert_to_numpy=True
)
print(f"生成された埋め込み数: {len(embeddings)}")
print(f"メモリ使用量削減: バッチ処理により、一度に処理するテキスト数が制限されます")
バッチサイズを調整することで、メモリ使用量とスループットのバランスを取ることができます。小さいバッチサイズ(例:8)ならメモリ消費が少ないですが処理が遅く、大きいバッチサイズ(例:256)なら高速ですがメモリが必要になります。
Claude Codeで実装する実践例
実装パターン1:簡単な埋め込み生成スクリプト
Claude Codeで以下の要件を指示すると、適切なコードが生成されます。
Claude Codeへの指示例:
以下の条件で埋め込み生成スクリプトを書いてください:
1. HuggingFaceの'all-MiniLM-L6-v2'モデルを使用
2. TOKENIZERS_PARALLELISM警告を出さない設定
3. キャッシュディレクトリを'./models'に指定
4. 入力テキストはCSVファイルから読み込む
5. 出力は埋め込みベクトルをNumPy形式で保存
Claude Codeが生成するコード例:
import os
import csv
import numpy as np
from sentence_transformers import SentenceTransformer
# 環境設定
os.environ["TOKENIZERS_PARALLELISM"] = "false"
os.environ["HF_HOME"] = "./models"
def load_texts_from_csv(csv_path):
"""CSVファイルからテキストを読み込む"""
texts = []
with open(csv_path, 'r', encoding='utf-8') as f:
reader = csv.reader(f)
next(reader) # ヘッダーをスキップ
for row in reader:
if row:
texts.append(row[0])
return texts
def generate_embeddings(texts, output_path='embeddings.npy'):
"""テキストから埋め込みを生成して保存"""
model = SentenceTransformer('all-MiniLM-L6-v2')
embeddings = model.encode(
texts,
batch_size=32,
show_progress_bar=True,
convert_to_numpy=True
)
np.save(output_path, embeddings)
print(f"埋め込みを {output_path} に保存しました")
print(f"形状: {embeddings.shape}")
return embeddings
if __name__ == "__main__":
# テキストを読み込む
texts = load_texts_from_csv('input.csv')
print(f"読み込んだテキスト数: {len(texts)}")
# 埋め込みを生成
embeddings = generate_embeddings(texts)
実装パターン2:LangChainと組み合わせた使用例
LangChainを使うと、HuggingFaceの埋め込みモデルをより簡単に統合できます。
import os
from langchain_community.embeddings import HuggingFaceEmbeddings
os.environ["TOKENIZERS_PARALLELISM"] = "false"
# HuggingFaceの埋め込みを初期化
embeddings = HuggingFaceEmbeddings(
model_name='all-MiniLM-L6-v2',
model_kwargs={'device': 'cpu'}, # または 'cuda'
encode_kwargs={'normalize_embeddings': True}
)
# テキストを埋め込み
texts = ["これはテキスト1です", "これはテキスト2です"]
text_embeddings = embeddings.embed_documents(texts)
print(f"埋め込み数: {len(text_embeddings)}")
print(f"各埋め込みの次元数: {len(text_embeddings[0])}")
LangChainを使う場合、pip install langchain-community でインストールしてください。
つまずきやすいポイントと解決策
問題1:CUDA対応GPUを使いたいが、PyTorchがCPU版でインストールされている
症状:埋め込み処理が非常に遅い、メモリ不足エラーが出やすい
解決策:
# 現在のPyTorchバージョン確認
pip show torch
# PyTorchを再インストール(CUDA 12.1対応版)
pip uninstall torch -y
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
インストール後、以下で確認:
import torch
print(torch.cuda.is_available()) # Trueなら成功
print(torch.cuda.get_device_name(0)) # GPU名を確認
問題2:モデルファイルがダウンロードできない、またはオフライン環境で動作させたい
症状:OSError: Can't find a cached version of 'all-MiniLM-L6-v2'
解決策:
import os
from sentence_transformers import SentenceTransformer
# オンライン環境で一度ダウンロード
os.environ["HF_HOME"] = "/path/to/cache"
model = SentenceTransformer('all-MiniLM-L6-v2')
# その後、キャッシュディレクトリごとオフライン環境にコピー
# オフライン環境では以下を実行
os.environ["HF_HOME"] = "/path/to/cache"
os.environ["HF_OFFLINE"] = "1"
model = SentenceTransformer('all-MiniLM-L6-v2')
問題3:メモリ不足で途中でクラッシュする
症状:RuntimeError: CUDA out of memory または MemoryError
解決策:
- バッチサイズを小さくする:
embeddings = model.encode(texts, batch_size=8) # デフォルト32から8に減らす
- モデルをfloat16に変換:
import torch
if torch.cuda.is_available():
model = model.half() # float32 → float16に変換
- より軽量なモデルに変更:
# all-mpnet-base-v2(430MB)から
# all-MiniLM-L6-v2(22MB)に変更
model = SentenceTransformer('all-MiniLM-L6-v2')
ChatGPT APIと組み合わせるハイブリッド構成
HuggingFaceのローカル埋め込みとOpenAI APIの埋め込みを使い分ける構成も考えられます。
import os
from sentence_transformers import SentenceTransformer
os.environ["TOKENIZERS_PARALLELISM"] = "false"
def get_local_embeddings(texts):
"""HuggingFaceで埋め込みを取得(ローカル、無料)"""
model = SentenceTransformer('all-MiniLM-L6-v2')
return model.encode(texts, convert_to_numpy=True)
def decide_embedding_method(text_length, is_sensitive=False):
"""テキスト長と機密性に基づいて埋め込み方法を決定"""
if is_sensitive or text_length < 1000:
# 機密情報、または短いテキストはローカルで処理
return "local"
else:
# 長いテキストや公開情報はAPIで処理
return "api"
# 使用例
texts = ["短いテキスト", "より長いテキスト" * 100]
is_sensitive = [False, True]
for text, sensitive in zip(texts, is_sensitive):
method = decide_embedding_method(len(text), sensitive)
if method == "local":
embeddings = get_local_embeddings([text])
print(f"ローカル処理: {len(text)}字")
else:
print(f"API処理: {len(text)}字")
応用例:検索エンジンの構築
HuggingFaceのローカル埋め込みを使って、シンプルな意味ベース検索(セマンティック検索)を実装できます。
import os
import numpy as np
from sentence_transformers import SentenceTransformer
from sklearn.metrics.pairwise import cosine_similarity
os.environ["TOKENIZERS_PARALLELISM"] = "false"
class SimpleSemanticSearch:
def __init__(self, model_name='all-MiniLM-L6-v2'):
self.model = SentenceTransformer(model_name)
self.documents = []
self.embeddings = None
def add_documents(self, documents):
"""ドキュメントを追加して埋め込みを生成"""
self.documents = documents
self.embeddings = self.model.encode(
documents,
show_progress_bar=False,
convert_to_numpy=True
)
def search(self, query, top_k=3):
"""クエリに最も似たドキュメントを検索"""
query_embedding = self.model.encode(query, convert_to_numpy=True)
# コサイン類似度を計算
similarities = cosine_similarity(
[query_embedding],
self.embeddings
)[0]
# スコアでソート
sorted_indices = np.argsort(similarities)[::-1][:top_k]
results = [
{
"document": self.documents[i],
"similarity": float(similarities[i])
}
for i in sorted_indices
]
return results
# 使用例
search_engine = SimpleSemanticSearch()
documents = [
"機械学習は人工知能の一分野です",
"自然言語処理はテキスト分析に使われます",
"ディープラーニングは神経網を模倣しています",
"画像認識はコンピュータビジョンの応用です"
]
search_engine.add_documents(documents)
# 検索実行
query = "テキスト処理技術について知りたい"
results = search_engine.search(query, top_k=2)
for result in results:
print(f"スコア: {result['similarity']:.3f}")
print(f"ドキュメント: {result['document']}\n")
このコードを実行すると、「自然言語処理はテキスト分析に使われます」が最も高いスコアで返されます。
次のステップ:本番環境への展開
ローカル埋め込み処理を本番環境に展開する場合、以下の点に注意します。
-
キャッシュディレクトリの永続化:Dockerコンテナを使う場合、
HF_HOMEをボリュームマウントして、毎回ダウンロードを避ける -
メモリプーリング:モデルを一度ロードして再利用し、毎回ロード/アンロードのコストを避ける
-
非同期処理:FastAPIなどのフレームワークで非同期実行(async/await)を使い、複数リクエストを処理
-
監視とログ:埋め込み生成の処理時間やメモリ使用量をモニタリング
これらの本番構成は別記事で詳しく扱う予定です。
あわせて読みたい
- Claude Code でローカルLLM(Ollama)を使う完全ガイド【無料・プライバシー重視・2026年最新】
- Claude CodeでMCPサーバー連携【2026年版】設定方法とPythonカスタムサーバー実装
- LangChain × ベクトルDB で RAG システムを実装|5ステップで自社データAIを構築