GoogleColabで複数のリトリーバーの結果を組み合わせる方法(Fate/stay night編)

AI・機械学習

はじめに

前回の記事「GoogleColabで複数のリトリーバーの結果を組み合わせる方法」では、EnsembleRetrieverを使用して複数のリトリーバーの結果をアンサンブルする基本的な方法について説明しました。本記事では、より応用的な使い方として、Fate/stay nightのドキュメントを使用して異なるリトリーバーを組み合わせる方法を紹介します。

異なるリトリーバーの組み合わせ

前回の記事では、同一のドキュメントリストを使用してBM25リトリーバーとFAISSリトリーバーを初期化しました。しかし、実際のシナリオでは、異なるリトリーバーを組み合わせることがあります。例えば、以下のような場合です。

  • スパースリトリーバーと密リトリーバーを組み合わせて、キーワードマッチングと意味的類似性の両方を考慮する場合
  • 異なる埋め込みモデル(例:BERT、RoBERTa、GPT-3など)を使用したリトリーバーを組み合わせる場合
  • 異なるアルゴリズム(例:BM25、TF-IDF、LSAなど)を使用したリトリーバーを組み合わせる場合

このような場合でも、EnsembleRetrieverを使用して複数のリトリーバーの結果をアンサンブルすることができます。以下では、BM25リトリーバーと2つの異なる埋め込みモデルを使用したFAISSリトリーバーを組み合わせる方法を示します。

セットアップ

以下では、BM25RetrieverとFAISSベクトルストアから派生したリトリーバーのアンサンブルを示します。

まず、必要なライブラリをインストールします。

# rank_bm25ライブラリをインストール
!pip install --upgrade --quiet  rank_bm25
!pip install langchain_community
!pip install langchain_openai
!pip install langchain_core
!pip install faiss-gpu

from google.colab import userdata
import os
os.environ['OPENAI_API_KEY'] = userdata.get('OPENAI_API_KEY')

次に、必要なモジュールをインポートします。

# EnsembleRetrieverをインポート
from langchain.retrievers import EnsembleRetriever
# BM25Retrieverをインポート
from langchain_community.retrievers import BM25Retriever
# FAISSベクトルストアをインポート
from langchain_community.vectorstores import FAISS
# OpenAIの埋め込みモデルをインポート
from langchain_openai import OpenAIEmbeddings

サンプルコード

まず、ドキュメントリストを用意します。

fate_doc_list_1 = [
    "日本のとある地方都市「冬木市」に数十年に一度現れるとされる、持ち主のあらゆる願いを叶える「聖杯」。7人の魔術師(マスター)は7騎の使い魔(サーヴァント)と契約し、聖杯を巡る抗争「聖杯戦争」に臨む。",
    "高校二年生の冬に、遠坂凛は、前回の聖杯戦争で命を落とした父の遺志を継ぎ、聖杯戦争に挑もうとしていた。凛は男性のサーヴァントを召喚するが、彼は年若い魔術師である凛を侮る態度に出たため、思い通りに動かそうと回数制限のある絶対命令権である令呪を使用するという暴挙に出てしまう。",
    "士郎は10年前(1994年)に起きた冬木大火災の生き残りで、養父の跡を継ぎそこなった半人前の魔術師だった。高校生になっていた士郎はある日、夜の学校で偶然にもアーチャーとランサーによるサーヴァント戦を目撃したことから、ランサーに殺されかかるが駆けつけた凛によって蘇生魔術を施され帰宅する。",
    "士郎とセイバーは様々な強敵たちと対抗するため、凛やアーチャーと共同戦線を張ることとなる。激闘の末、セイバーは「約束された勝利の剣(エクスカリバー)」を使用して間桐慎二のサーヴァントであるライダーを撃破。",
    "士郎は単身、8人目のサーヴァントというイレギュラーについて監督役の言峰綺礼に聞き出そうとするが、その言峰こそがギルガメッシュのマスターであり、また残るランサーも言峰がほかのマスターから略奪したサーヴァントであったことが判明する。",
    "紆余曲折の末、凛やアーチャーと同盟を結ぶことになった士郎とセイバーだったが、士郎はアーチャーと反りが合わず、アーチャーからも「理想を抱いて溺死しろ」とまで告げられる。それでも士郎は何故かアーチャーの刀や剣技に惹かれていくのだった。",
    "アーチャーの正体は未来において英雄になった衛宮士郎であること、またその末路を聞かされ、「お前の理想は間違いだった」と告げられるが、士郎はそれを否定し対決する。士郎はギルガメッシュの圧倒的な力に劣勢を強いられるが、アーチャーの固有結界である「無限の剣製」を自身の手で発動させたことによって形勢を逆転させ、ギルガメッシュを追い詰める。",
    "なおも探索を続けていた士郎とセイバーだったが、セイバーが真アサシンに敗れ、黒い影に取り込まれてしまう。サーヴァントを失った士郎だったが、ライダーが実は桜のサーヴァントであったということ、そして桜とその命は臓硯の手中にあるということを知らされる。",
    "間桐臓硯は聖杯として膨大な魔力を得た桜に離反され、真アサシンも桜によって殺害される。桜を救いたいというライダーの協力を得た士郎は、セイバーを激闘の末に倒す。士郎は言峰と激闘を繰り広げ圧倒されるが、言峰もまた既に限界を突破しており、その戦いの最中に息絶える。",
    "冬木の聖杯は聖堂教会に観測された第726個目の聖杯候補であり、表向きは真贋の判断が付いていないことになっている。しかし、実は失われた第三魔法・魂の物質化、天の杯を再現するために作られた贋作であるとの判定はできている。"
]

次に、BM25リトリーバーと2つのFAISSリトリーバーを初期化します。ここでは、FAISSリトリーバーにはOpenAIの埋め込みモデルとSentence-BERTの埋め込みモデルを使用します。

# BM25リトリーバーを初期化
# doc_list_1からBM25リトリーバーを作成し、各ドキュメントにメタデータとしてsource=1を付与
bm25_retriever = BM25Retriever.from_texts(
    fate_doc_list_1, metadatas=[{"source": 1}] * len(fate_doc_list_1)
)
# 検索結果の数を2に設定
bm25_retriever.k = 2

# FAISSリトリーバーを初期化
# OpenAIの埋め込みモデルを使用
embedding = OpenAIEmbeddings()
# doc_list_2からFAISSベクトルストアを作成し、各ドキュメントにメタデータとしてsource=2を付与
faiss_vectorstore = FAISS.from_texts(
    fate_doc_list_1, embedding, metadatas=[{"source": 2}] * len(fate_doc_list_1)
)
# FAISSベクトルストアからリトリーバーを作成し、検索結果の数を2に設定
faiss_retriever = faiss_vectorstore.as_retriever(search_kwargs={"k": 2})

アンサンブルリトリーバーを初期化し、BM25リトリーバーと2つのFAISSリトリーバーを組み合わせます。

# アンサンブルリトリーバーを初期化
# BM25リトリーバーとFAISSリトリーバーを使用し、それぞれの重みを0.5に設定
ensemble_retriever = EnsembleRetriever(
    retrievers=[bm25_retriever, faiss_retriever], weights=[0.5, 0.5]
)

アンサンブルリトリーバーを使用してドキュメントを取得します。

# "セイバー"をクエリとしてアンサンブルリトリーバーを実行
docs = ensemble_retriever.invoke("セイバー")
for doc in docs:
  print("-"*50)
  print(doc.metadata)
  print(doc.page_content)
--------------------------------------------------
{'source': 1}
冬木の聖杯は聖堂教会に観測された第726個目の聖杯候補であり、表向きは真贋の判断が付いていないことになっている。しかし、実は失われた第三魔法・魂の物質化、天の杯を再現するために作られた贋作であるとの判定はできている。
--------------------------------------------------
{'source': 2}
士郎とセイバーは様々な強敵たちと対抗するため、凛やアーチャーと共同戦線を張ることとなる。激闘の末、セイバーは「約束された勝利の剣(エクスカリバー)」を使用して間桐慎二のサーヴァントであるライダーを撃破。
--------------------------------------------------
{'source': 1}
間桐臓硯は聖杯として膨大な魔力を得た桜に離反され、真アサシンも桜によって殺害される。桜を救いたいというライダーの協力を得た士郎は、セイバーを激闘の末に倒す。士郎は言峰と激闘を繰り広げ圧倒されるが、言峰もまた既に限界を突破しており、その戦いの最中に息絶える。
--------------------------------------------------
{'source': 2}
なおも探索を続けていた士郎とセイバーだったが、セイバーが真アサシンに敗れ、黒い影に取り込まれてしまう。サーヴァントを失った士郎だったが、ライダーが実は桜のサーヴァントであったということ、そして桜とその命は臓硯の手中にあるということを知らされる。

BM25リトリーバーと2つのFAISSリトリーバーを組み合わせることで、キーワードマッチングと意味的類似性の両方を考慮した検索結果が得られています。また、異なる埋め込みモデルを使用することで、より多様な検索結果が返されていることがわかります。

まとめ

本記事では、異なるリトリーバーをEnsembleRetrieverを使用して組み合わせる方法について説明しました。この手法は、以下のようなシナリオで役立ちます。

  • スパースリトリーバーと密リトリーバーを組み合わせて、キーワードマッチングと意味的類似性の両方を考慮する場合
  • 異なる埋め込みモデル(例:BERT、RoBERTa、GPT-3など)を使用したリトリーバーを組み合わせる場合
  • 異なるアルゴリズム(例:BM25、TF-IDF、LSAなど)を使用したリトリーバーを組み合わせる場合

異なるリトリーバーを組み合わせることで、それぞれのリトリーバーの長所を活かした検索結果を得ることができます。実際のプロジェクトでは、タスクや目的に応じて最適なリトリーバーの組み合わせを見つけることが重要です。

本記事で紹介したサンプルコードを参考に、異なるリトリーバーの組み合わせをGoogleColabで試してみてください。より高度な検索システムを構築するために、EnsembleRetrieverの応用的な使い方を理解することをおすすめします。

📒 ノートブック

Google Colab

参考サイト

How to combine results from multiple retrievers | 🦜️🔗 LangChain
The EnsembleRetriever supports ensembling of results from multiple retrievers. It is initialized with a list of BaseRetriever objects. EnsembleRetrievers rerank...
LangChain で RAGのハイブリッド検索 を試す|npaka
「LangChain」でRAGのハイブリッド検索を試したので、まとめました。 ・langchain v0.2.0 1. RAGのハイブリッド検索 「RAG」のハイブリッド検索は、複数の検索方法を組み合わせる手法で、主に「ベクトル検索」と「キーワード検索」を組み合わせて使います。 ・ベクトル検索 文書をベ...

コメント

タイトルとURLをコピーしました