こんにちは、たねやつです。
前回は、RAGシステムのための重要な下準備として、育児ログデータを「1日ごと」のチャンクに分割する「チャンキング」を行いました。これにより、私たちの知識ベースは、検索しやすいように1ページずつ区切られた本のようになりました。
しかし、コンピュータは「ミルク」や「睡眠」といった単語の意味を、そのままでは理解できません。人間が言葉の意味を理解できるように、コンピュータにもテキストを理解できる形式、つまり「数値」の形式で与える必要があります。
今回は、このテキストから意味を抽出して数値のベクトルに変換する、エンベディング(Embedding) というプロセスを実装します。これは、AIが言葉の"意味"を理解するための、まさに魔法のような技術です。
この記事でできること
- テキストエンベディングが何であり、なぜRAGシステムに不可欠なのかを理解できる。
- 日本語のテキストを扱う際に、どのようなエンベディングモデルを選べばよいかの指針がわかる。
sentence-transformersというライブラリを使い、前回作成したテキストチャンクを数値ベクトルに変換する具体的なコードを実装できる。
事前に必要なもの
- Pythonの実行環境
- 関連ライブラリ:
sentence-transformersと、その動作に必要となるtorchをインストールします。bash pip install sentence-transformers torch - 前回の成果物: 前回作成した日ごとのテキストチャンク。
テキストエンベディングとは?
テキストエンベディングとは、一言でいうと「文章の意味を、多次元空間の点(ベクトル)として表現する技術」です。
例えば、「今日のミルクの量は?」という文章と、「今日の授乳回数は?」という文章を考えてみましょう。使われている単語は異なりますが、意味は非常に似ていますよね。エンベディングモデルは、このような意味の類似性を捉え、2つの文章をベクトル空間上の「ご近所さん」として配置します。
- 似ている文章 → ベクトル空間上で近い位置に
- 似ていない文章 → ベクトル空間上で遠い位置に
この仕組みを利用することで、コンピュータはキーワードの一致だけでなく、文脈や意味の近さに基づいて情報を検索できるようになります。これが、RAGシステムがユーザーの曖昧な質問にも的確に答えられる秘密なのです。
日本語エンベディングモデルの選定
エンベディングの性能は、使用する「モデル」によって大きく左右されます。特に日本語は、英語などと比べて言語構造が複雑なため、モデル選びが重要になります。
今回は、Hugging FaceというAIモデルのハブサイトで公開されており、sentence-transformers ライブラリで簡単に利用できる、日本語に強いと評判のモデルの中から選んでみましょう。
候補としては、以下のようなモデルが挙げられます。
distiluse-base-multilingual-cased-v1: 多言語対応の汎用モデル。手軽に試せるのが魅力。cl-tohoku/bert-base-japanese-whole-word-masking: 東北大学が開発した日本語特化モデル。高い精度が期待できる。intfloat/multilingual-e5-large: 近年、様々なベンチマークで高い性能を示している多言語モデル。
今回は、導入の手軽さと日本語への対応力のバランスが良い intfloat/multilingual-e5-large を採用することにします。このモデルは、様々な言語で高い性能を発揮することが知られており、私たちの育児ログのような少し特殊な語彙が含まれるテキストでも、頑健に意味を捉えてくれることが期待できます。
実装:チャンクをベクトルに変換する
それでは、sentence-transformers を使って、前回作成した日ごとのテキストチャンクをエンベディングしてみましょう。embeddings.py という新しいファイルを作成します。
# embeddings.py from sentence_transformers import SentenceTransformer import numpy as np # 1. モデルの読み込み # 初回実行時は、モデルのダウンロードに時間がかかる場合があります。 print("モデルを読み込んでいます... (intfloat/multilingual-e5-large)") model = SentenceTransformer('intfloat/multilingual-e5-large') print("モデルの読み込み完了。") # 2. チャンクの読み込み print("チャンクを 'piyolog_chunks.txt' から読み込んでいます...") separator = separator = "---次の日の記録---" with open('piyolog_chunks.txt', 'r', encoding='utf-8') as f: content = f.read() # テキストをセパレータで分割し、空の要素があれば取り除く chunks = list(filter(None, content.split(separator))) print(f"{len(chunks)}個のチャンクを読み込みました。") # 3. エンベディングの実行 # モデルによっては、入力テキストに特定のプレフィックス(接頭辞)を付けることが推奨されています。 # multilingual-e5-largeでは、検索対象の文章(passage)には "passage: " を付けます。 chunks_with_prefix = ["passage: " + chunk for chunk in chunks] print("エンベディングを計算中...(チャンクの数によっては時間がかかります)") embeddings = model.encode(chunks_with_prefix, normalize_embeddings=True, show_progress_bar=True) print("エンベディングの計算完了。") # 4. 結果の確認 print(f"\n生成されたベクトルの数: {embeddings.shape[0]}") print(f"各ベクトルの次元数: {embeddings.shape[1]}") print("\n最初のチャンクのベクトル(先頭10次元のみ表示):") print(embeddings[0][:10]) # 5. ベクトルの保存 # 後のステップで使えるように、ベクトルをNumpy配列(.npy)形式で保存します。 np.save('piyolog_embeddings.npy', embeddings) print("\nエンベディングを 'piyolog_embeddings.npy' に保存しました。")
このスクリプトを実行すると、まずintfloat/multilingual-e5-largeモデルがHugging Faceからダウンロードされます(初回のみ)。その後、piyolog_chunks.txtからチャンクが読み込まれ、一つずつベクトルに変換されていきます。進捗バーが表示されるので、処理の進み具合を確認できます。
最終的に、piyolog_embeddings.npyというファイルが生成されます。このファイルには、全チャンクに対応するベクトルがNumpy配列として格納されています。元のチャンクはpiyolog_chunks.txtに、そのベクトルはpiyolog_embeddings.npyに、という形で対応が取れている状態です。
最後に
今回は、RAGシステムの心臓部とも言える「エンベディング」を実装しました。文章という曖昧なものを、「ベクトル」というコンピュータが計算できる明確な形式に変換することで、私たちはAIに言葉の意味を教える第一歩を踏み出しました。
- テキストをベクトルに変換する「エンベディング」
- 日本語に適したモデルの選定
sentence-transformersによる実装
これらのステップを経て、私たちの育児ログは、ついにAIが検索できる「知識」の形になりました。
しかし、現状ではベクトルはただのリスト(Numpy配列)としてメモリ上にあるだけです。これでは、質問が来るたびに全ての知識ベクトルと類似度を計算する必要があり、データが増えると非常に非効率です。
そこで次回は、この大量のベクトルを効率的に保存し、高速に検索できるようにするための専用のデータベース、「ベクトルデータベース」 を構築していきます。