こんにちは、たねやつです。
ローカルで動くLLMは非常に強力ですが、学習データに含まれない情報や、最新の情報については答えることができず、もっともらしい嘘(ハルシネーション)をついてしまうことがあります。
この課題を解決する技術として注目されているのが、RAG (Retrieval-Augmented Generation) です。これは、LLMが回答を生成する(Generation)前に、関連する情報を外部の知識源から検索(Retrieval)し、その内容を参考にして回答を生成する仕組みです。
この連載では、3回にわたってRAGによるQ&Aシステムをローカル環境に構築する方法を解説します。第1回となる今回は、RAGの心臓部である「知識源(ベクトルデータベース)」の構築に挑戦します。
- この記事でできること
- 準備するもの
- ステップ1: テキストの読み込みと分割
- ステップ2: EmbeddingGemmaによるベクトル化
- ステップ3: FAISSによるベクトルデータベースの構築と保存
- 全体のコード
- まとめ
この記事でできること
- RAG(Retrieval-Augmented Generation)の基本的な概念が理解できます。
- 手持ちのテキスト文書を、AIが理解できる「ベクトル」に変換する方法を学べます。
- 高性能な埋め込みモデル
EmbeddingGemmaと、高速なベクトル検索ライブラリFAISSを使って、ベクトルデータベースを構築・保存できるようになります。
準備するもの
1. Pythonライブラリ
以下のライブラリをインストールします。
# sentence-transformers: EmbeddingGemmaを簡単に使うため # faiss-cpu: ベクトルデータベースを構築・検索するため (GPU版はfaiss-gpu) # numpy: FAISSでベクトルを扱うため pip install -U sentence-transformers faiss-cpu numpy
2. 知識源となるテキストファイル
今回は、AIに関する架空の解説テキストを3つ用意しました。knowledgeというフォルダを作成し、その中に以下の3つのファイルを保存してください。
knowledge/01_llm.txt
大規模言語モデル(LLM)は、膨大なテキストデータを用いてトレーニングされた人工知能モデルです。人間のように自然な文章を生成したり、要約したり、質問に答えたりする能力を持っています。代表的なモデルに、OpenAIのGPTシリーズや、GoogleのGeminiなどがあります。
knowledge/02_rag.txt
RAG(Retrieval-Augmented Generation)は、LLMの回答精度を向上させるための技術です。ユーザーからの質問に対し、まず関連する情報をデータベースから検索(Retrieval)し、その検索結果を基にLLMが回答を生成(Generation)します。これにより、LLMが知らない情報についても、正確な回答を生成できるようになります。
knowledge/03_embedding.txt
埋め込み(Embedding)とは、単語や文章を「ベクトル」と呼ばれる数値の配列に変換する技術です。このベクトルは、単語や文章の意味的な近さを表現しており、意味が近いほどベクトル空間上での距離が近くなります。セマンティック検索やRAGの文書検索など、様々な応用で利用されています。
ステップ1: テキストの読み込みと分割
まず、用意したテキストファイルをプログラムに読み込みます。今回は簡単のため、1ファイル全体を1つの文書(ドキュメント)として扱います。
import os import numpy as np from sentence_transformers import SentenceTransformer import faiss # 知識源となるテキストファイルが格納されているディレクトリ KNOWLEDGE_DIR = "knowledge" # ドキュメントを格納するリスト documents = [] # ディレクトリ内の全ファイルをループ for filename in os.listdir(KNOWLEDGE_DIR): if filename.endswith(".txt"): filepath = os.path.join(KNOWLEDGE_DIR, filename) with open(filepath, "r", encoding="utf-8") as f: documents.append(f.read()) print(f"{len(documents)}個のドキュメントを読み込みました。") # 出力例: 3個のドキュメントを読み込みました。
ステップ2: EmbeddingGemmaによるベクトル化
次に、読み込んだ各ドキュメントをEmbeddingGemmaを使ってベクトルに変換します。sentence-transformersを使えば、数行のコードで簡単に実現できます。
# 埋め込みモデルのロード model = SentenceTransformer("google/embeddinggemma-300m") # ドキュメントをベクトル化 print("ドキュメントをベクトル化しています...") embeddings = model.encode(documents) # ベクトルの形状を確認 print(f"ベクトル化完了。Embeddings shape: {embeddings.shape}") # 出力例: ベクトル化完了。Embeddings shape: (3, 768) # (3つのドキュメントが、それぞれ768次元のベクトルに変換されたことを示す)
ステップ3: FAISSによるベクトルデータベースの構築と保存
最後に、ベクトル化したデータをFAISSに登録し、インデックスを作成します。FAISSは、大量のベクトルデータから高速に類似ベクトルを検索するためのライブラリです。
# FAISSインデックスの構築 # ベクトルの次元数を取得 d_model = embeddings.shape[1] # IndexFlatL2: L2距離(ユークリッド距離)で類似度を計算する最も基本的なインデックス index = faiss.IndexFlatL2(d_model) # ベクトルをインデックスに追加 index.add(embeddings.astype('float32')) # FAISSはfloat32を要求 print(f"FAISSインデックスに{index.ntotal}個のベクトルを追加しました。") # インデックスの保存 INDEX_FILE = "faiss_index.bin" faiss.write_index(index, INDEX_FILE) print(f"インデックスを'{INDEX_FILE}'に保存しました。")
これで、faiss_index.binというファイルが作成されていれば成功です。このファイルに、3つのドキュメントがベクトル化されて格納されています。
全体のコード
ここまでのステップをまとめた、ベクトルデータベースを構築・保存するための全体のコードは以下のようになります。
import os import numpy as np from sentence_transformers import SentenceTransformer import faiss # --- 1. テキストの読み込みと分割 --- KNOWLEDGE_DIR = "knowledge" documents = [] print(f"'{KNOWLEDGE_DIR}'からドキュメントを読み込んでいます...") for filename in os.listdir(KNOWLEDGE_DIR): if filename.endswith(".txt"): filepath = os.path.join(KNOWLEDGE_DIR, filename) with open(filepath, "r", encoding="utf-8") as f: documents.append(f.read()) print(f"{len(documents)}個のドキュメントを読み込みました。") # --- 2. EmbeddingGemmaによるベクトル化 --- model = SentenceTransformer("google/embeddinggemma-300m") print("ドキュメントをベクトル化しています...") embeddings = model.encode(documents) print(f"ベクトル化完了。Embeddings shape: {embeddings.shape}") # --- 3. FAISSによるベクトルデータベースの構築と保存 --- d_model = embeddings.shape[1] index = faiss.IndexFlatL2(d_model) index.add(embeddings.astype('float32')) print(f"FAISSインデックスに{index.ntotal}個のベクトルを追加しました。") INDEX_FILE = "faiss_index.bin" faiss.write_index(index, INDEX_FILE) print(f"インデックスを'{INDEX_FILE}'に保存しました。")
まとめ
今回は、RAGシステムの基礎となるベクトルデータベースを構築しました。手持ちのテキストをEmbeddingGemmaでベクトル化し、FAISSに登録・保存するまでの一連の流れを解説しました。
次回の【実践編】では、今回作成したデータベースを使い、ユーザーからの質問に最も関連性の高い文書を検索する「Retriever」を実装します。