GoogleColabで複数のリトリーバーの結果を組み合わせる方法(ノート付)

AI・機械学習

GoogleColabで複数のリトリーバーの結果を組み合わせる方法

はじめに

EnsembleRetrieverとは

EnsembleRetrieverは、複数の異なるリトリーバーの結果を組み合わせて、より高精度な検索結果を得るためのクラスです。以下の図は、EnsembleRetrieverの概要を示しています。

file

EnsembleRetrieverは、BaseRetrieverを継承した複数のリトリーバーオブジェクトを受け取り、それぞれのリトリーバーの検索結果をアンサンブルします。アンサンブルには、Reciprocal Rank Fusion(RRF)アルゴリズムが使用されます。RRFは、各リトリーバーが返す結果の順位を考慮し、最終的な順位を決定します。

リトリーバーの種類

EnsembleRetrieverで組み合わせるリトリーバーには、主に2種類あります。

  1. スパースリトリーバー(例: BM25)

    • キーワードベースの検索に適しています。
    • ドキュメント内の単語の出現頻度と、クエリとの関連性を考慮します。
  2. 密リトリーバー(例: 埋め込み類似度)

    • 意味的類似性に基づく検索に適しています。
    • ドキュメントとクエリの分散表現(埋め込み)を比較し、類似度を計算します。

スパースリトリーバーと密リトリーバーを組み合わせることで、それぞれの長所を活かした検索が可能になります。この組み合わせは「ハイブリッド検索」とも呼ばれています。

リトリーバーの種類 検索の基準 長所 短所
スパースリトリーバー キーワードの出現頻度と関連性 計算コストが低い
キーワードマッチングに強い
同義語や関連語への対応が弱い
密リトリーバー 意味的類似性 同義語や関連語への対応が強い 計算コストが高い
埋め込みの質に依存

EnsembleRetrieverを使用することで、これらのリトリーバーの長所を組み合わせ、より高精度な検索結果を得ることができます。

基本的な使い方

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

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

# rank_bm25ライブラリをインストール
%pip install --upgrade --quiet  rank_bm25 > /dev/null

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

# 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

サンプルのドキュメントリストを用意します。

# BM25Retrieverで使用する日本語のドキュメントリスト
doc_list_1 = [
    "私はリンゴが好きです",
    "私はオレンジが好きです",
    "リンゴとオレンジは果物です",
]

# FAISSリトリーバーで使用する日本語のドキュメントリスト
doc_list_2 = [
    "あなたはリンゴが好きです",
    "あなたはオレンジが好きです",
]

BM25リトリーバーとFAISSリトリーバーを初期化します。

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

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

アンサンブルリトリーバーを初期化します。

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

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

# "リンゴ"をクエリとしてアンサンブルリトリーバーを実行
docs = ensemble_retriever.invoke("リンゴ")
# 検索結果を表示
print(docs)
[Document(page_content='リンゴとオレンジは果物です', metadata={'source': 1}), 
Document(page_content='あなたはリンゴが好きです', metadata={'source': 2}), 
Document(page_content='私はオレンジが好きです', metadata={'source': 1}), 
Document(page_content='あなたはオレンジが好きです', metadata={'source': 2})]

ランタイム設定

個々のリトリーバーをランタイム時に設定可能なフィールドを使用して設定することもできます。以下では、FAISSリトリーバーの"top-k"パラメータを更新します。

# ConfigurableFieldをインポート
from langchain_core.runnables import ConfigurableField

# FAISSリトリーバーを設定可能なフィールドで初期化
faiss_retriever = faiss_vectorstore.as_retriever(
    search_kwargs={"k": 2}
).configurable_fields(
    # 検索時のパラメータを設定可能なフィールドとして定義
    search_kwargs=ConfigurableField(
        id="search_kwargs_faiss",
        name="Search Kwargs",
        description="The search kwargs to use",
    )
)

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

設定を使用してドキュメントを取得します。

# FAISSリトリーバーの検索結果の数を1に設定
config = {"configurable": {"search_kwargs_faiss": {"k": 1}}}
# "リンゴ"をクエリとしてアンサンブルリトリーバーを実行し、設定を適用
docs = ensemble_retriever.invoke("リンゴ", config=config)
# 検索結果を表示
print(docs)
[Document(page_content='リンゴとオレンジは果物です', metadata={'source': 1}), 
Document(page_content='あなたはリンゴが好きです', metadata={'source': 2}), 
Document(page_content='私はオレンジが好きです', metadata={'source': 1})]

FAISSリトリーバーの設定をランタイム時に関連する設定を渡したため、FAISSリトリーバーからは1つのソースしか返されないことに注意してください。

まとめ

本記事では、初心者でも理解できるように、GoogleColabで複数のリトリーバーの結果を組み合わせる方法について説明しました。

EnsembleRetrieverとは

  • 複数の異なるリトリーバーの結果を組み合わせて、より高精度な検索結果を得るためのクラス
  • Reciprocal Rank Fusion(RRF)アルゴリズムを使用してアンサンブル
  • スパースリトリーバー(BM25など)と密リトリーバー(埋め込み類似度など)を組み合わせることが一般的

基本的な使い方

  1. 必要なライブラリをインストールし、モジュールをインポート
  2. サンプルのドキュメントリストを用意
  3. BM25リトリーバーとFAISSリトリーバーを初期化
  4. アンサンブルリトリーバーを初期化
  5. アンサンブルリトリーバーを使用してドキュメントを取得

ランタイム設定

  • 個々のリトリーバーをランタイム時に設定可能なフィールドを使用して設定可能
  • ConfigurableFieldを使用してFAISSリトリーバーの"top-k"パラメータを更新
  • 設定を使用してドキュメントを取得

EnsembleRetrieverを使用することで、異なるリトリーバーの長所を組み合わせ、より高精度な検索結果を得ることができます。本記事で紹介したサンプルコードを参考に、実際にGoogleColabで試してみることをおすすめします。APIリファレンスも参照しながら、様々な設定を試し、最適な組み合わせを見つけてください。

ノートブック

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をコピーしました