powered by TechFeed
表示モード
ハウツー

LangChainもベクトルDBも不要 — scikit-learnだけでRAGパイプラインを自作する方法

10月13日、Machine Learning Mastery(機械学習の実践的チュートリアルを発信するメディア)が「Building a RAG Pipeline with LLM Embeddings and Scikit-learn」と題した記事を公開した。RAGの本質はテキストをベクトル化して距離計算するだけであり、PineconeやLangChainといった専用インフラを使わずとも、scikit-learnとオープンソースモデルだけで軽量なRAGパイプラインは十分に成立する——これが記事の核心的な主張だ。中小規模のコーパスやPoC段階であれば、追加の依存ライブラリは不要である。

10月13日、Machine Learning Mastery(機械学習の実践的チュートリアルを発信するメディア)が「Building a RAG Pipeline with LLM Embeddings and Scikit-learn」と題した記事を公開した。RAGの本質はテキストをベクトル化して距離計算するだけであり、PineconeやLangChainといった専用インフラを使わずとも、scikit-learnとオープンソースモデルだけで軽量なRAGパイプラインは十分に成立する——これが記事の核心的な主張だ。中小規模のコーパスやPoC段階であれば、追加の依存ライブラリは不要である。


scikit-learnをベクトルDBの代替として使う

RAG(Retrieval-Augmented Generation)は、LLMに外部の知識ベースを参照させることで、ハルシネーションを減らしつつ回答精度を高める手法だ。一般的な実装ではPineconeやWeaviateといったマネージドベクトルデータベース、あるいはLangChainやLlamaIndexといったフレームワークが使われることが多い。

しかしこれらは、仕組みを理解したい段階や小規模なPoCには過剰だ。そこで登場するのがsklearn.neighbors.NearestNeighborsだ。コサイン距離を用いてベクトル間の類似度を計算するこのクラスは、ベクトルDBの代替として十分機能する。

記事では以下のようにSklearnRetrieverクラスとして実装している:

class SklearnRetriever:
    def __init__(self, n_neighbors=3):
        self.retriever = NearestNeighbors(n_neighbors=n_neighbors, metric='cosine', algorithm='brute')
        self.chunks = []

    def fit(self, chunks, embeddings):
        self.chunks = chunks
        self.retriever.fit(embeddings)

    def retrieve(self, query_embedding):
        distances, indices = self.retriever.kneighbors([query_embedding])
        return [self.chunks[idx] for idx in indices[0]]

metric='cosine'を指定するのは、LLMの埋め込みベクトルが「ベクトルの向き(角度)」で意味的類似度を表現するためだ。2つのベクトルが同じ方向を向いているほど類似度が高い(コサイン距離が0に近い)と判断される。ユークリッド距離ではベクトルの大きさ(ノルム)が異なる場合に誤判定しやすく、文書の長短が影響してしまうためコサイン距離が適切だ。

なお、algorithm='brute'の指定は必須である。scikit-learnのNearestNeighborsはball_treeやkd_treeといった高速な近似アルゴリズムも選択できるが、これらはコサイン距離に対応していない。metric='cosine'を使う場合は全件探索となる'brute'を明示しなければエラーになるため、自分でコードを変更する際は注意が必要だ。


パイプラインの全構成

記事で紹介されるパイプラインは以下の3ステップで構成される。実行環境はGoogle ColabのT4 GPU(無料)を推奨している。

1. データ準備とチャンキング

アジア各国の概要を記した9つのテキストファイル(GitHubで公開)をデータセットとして使用する。テキストはそのままではなく、チャンク(小分け)に分割してからEmbeddingモデルに渡すのがRAGの定石だ。記事では単語数ベースの素朴なチャンク関数を実装し、9ドキュメントから31チャンクを生成している:

def chunk_text(text, chunk_size=25, overlap=5):
    words = text.split()
    chunks = []
    for i in range(0, len(words), chunk_size - overlap):
        chunk = " ".join(words[i:i + chunk_size])
        chunks.append(chunk)
    return chunks

overlap=5はチャンク間で5単語を重複させることで、境界付近の文脈が失われるのを防ぐ設定だ。

2. Embeddingの生成

埋め込みモデルには**all-MiniLM-L6-v2**(sentence-transformers)を使用する。軽量かつ品質が高く、ローカル実行に適した定番モデルだ:

embedding_model = SentenceTransformer('all-MiniLM-L6-v2')
chunk_embeddings = embedding_model.encode(all_chunks, show_progress_bar=True)

3. 生成モデルの接続

生成には**Qwen/Qwen2.5-0.5B-Instruct(Hugging Faceモデルページ)を使用する。0.5Bパラメータ**という小ささで、Colabの無料RAM上でも動作する。システムプロンプトで「提供されたコンテキストのみを使って回答すること」と明示することで、RAGとしての動作を担保している:

messages = [
    {"role": "system", "content": "Answer using ONLY the provided context."},
    {"role": "user", "content": f"Context:\n{context_str}\n\nQuestion: {query}"}
]

実際の出力例

「アジアで島やビーチで有名な国はどこか?」という質問に対し、パイプラインは以下のように回答している:

RAG Answer: The Asian countries that are best known for islands and beaches include:
1. Philippines - Known for its over 7,000 islands and vibrant festivals.
2. Thailand - Famous for its tropical beaches and bustling street food culture.

--- Sources used ---
1. mountainous landscapes and cultural cities like Chiang Mai, while the south offers world-renowned is...
2. The Philippines is an archipelago of over 7,000 islands in Southeast Asia, known for its stunning be...
3. Thailand is a Southeast Asian country famous for its tropical beaches, ornate temples, and bustling ...

参照したソースチャンクも出力されており、回答の根拠を追跡できる構成になっている点が実用上の利点だ。


使用ライブラリのまとめ

役割 ライブラリ/モデル
チャンキング 自前実装(標準ライブラリのみ)
Embedding sentence-transformers(all-MiniLM-L6-v2)
ベクトル検索 scikit-learn(NearestNeighbors)
テキスト生成 transformers(Qwen2.5-0.5B-Instruct)
実行環境 Google Colab(T4 GPU、無料)

有料サービスへの依存がゼロで、コードの各ステップが何をしているか明示的に追える構成は、RAGの仕組みを学ぶうえで既製フレームワークより理解しやすい。本番運用でスケールが必要になった際にPineconeやLangChainといった専用インフラへ移行するとしても、この構成でプロトタイプを素早く検証できるのは実践的なメリットだ。

詳細はBuilding a RAG Pipeline with LLM Embeddings and Scikit-learnを参照していただきたい。