LangChain で RAGのハイブリッド検索 を試す
「LangChain」でRAGのハイブリッド検索を試したので、まとめました。
1. RAGのハイブリッド検索
「RAG」のハイブリッド検索は、複数の検索方法を組み合わせる手法で、主に「ベクトル検索」と「キーワード検索」を組み合わせて使います。
2. LangChainの準備
LangChainの準備の手順は、次のとおりです。
(1) LangChainのパッケージのインストール。
# LangChainのパッケージのインストール
!pip install langchain==0.2.0
!pip install langchain-community
!pip install langchain-openai
(2) 左端の鍵アイコンで「OPENAI_API_KEY」に自分のOpenAI APIキーを設定してからセルを実行。
import os
from google.colab import userdata
# 環境変数の準備 (左端の鍵アイコンでOPENAI_API_KEYを設定)
os.environ["OPENAI_API_KEY"] = userdata.get("OPENAI_API_KEY")
3. ドキュメントの準備
ドキュメントの準備の手順は、次のとおりです。
(1) ドキュメントの準備。
# ドキュメントの準備
texts = [
"後藤ひとりはギターが上手",
"後藤ひとりの妹は後藤ふたり",
"虹夏の名字は伊地知",
]
4. ベクトル検索
ベクトル検索のRetrieverの作成手順は、次のとおりです。
(1) ベクトル検索のパッケージのインストール。
今回は「Chroma」を使います。
# ベクトル検索のパッケージ
!pip install langchain-chroma
!pip install unstructured
(2) 埋め込みモデルの準備。
from langchain_openai import OpenAIEmbeddings
# 埋め込みモデルの準備
embeddings = OpenAIEmbeddings()
(3) VectorStoreの準備。
from langchain_chroma import Chroma
# VectorStoreの準備
vectorstore = Chroma.from_texts(
texts,
embedding=embeddings,
)
(4) Retrieverの準備。
# Retrieverの準備
chroma_retriever = vectorstore.as_retriever(
search_type="similarity",
search_kwargs={"k": 1},
)
(5) Retrieverの動作確認。
今回は簡単すぎて全問正解でした。
# Retrieverの動作確認
chroma_retriever.invoke("ギターが得意なのは?")
[Document(page_content='後藤ひとりはギターが上手')]
# Retrieverの動作確認
chroma_retriever.invoke("後藤ふたりの姉は?")
[Document(page_content='後藤ひとりの妹は後藤ふたり')]
# Retrieverの動作確認
chroma_retriever.invoke("伊地知の名前は?")
[Document(page_content='虹夏の名字は伊地知')]
5. キーワード検索
キーワード検索のRetrieverの作成手順は、次のとおりです。
(1) キーワード検索のパッケージのインストール。
今回は「BM25」を使います。
# キーワード検索のパッケージのインストール
!pip install rank_bm25
!pip install sudachipy sudachidict_full
(2) トークン化関数の準備。
次に説明する「BM25Retriever」はデフォルトで、文章をスペースでトークン化して処理ます。英語はこれでトークン化できますが、日本語はできないので、sudachiで分かち書きする関数を準備します。
from langchain.retrievers import BM25Retriever
from typing import List
from sudachipy import tokenizer
from sudachipy import dictionary
# トークン化関数の準備
def preprocess_func(text: str) -> List[str]:
tokenizer_obj = dictionary.Dictionary(dict="full").create()
mode = tokenizer.Tokenizer.SplitMode.A
tokens = tokenizer_obj.tokenize(text ,mode)
words = [token.surface() for token in tokens]
words = list(set(words)) # 重複削除
return words
(3) Retrieverの準備。
今回は「BM25Retriever」を使います。
from langchain_community.retrievers import BM25Retriever
# Retrieverの準備
bm25_retriever = BM25Retriever.from_texts(
texts,
preprocess_func=preprocess_func,
k=1,
)
(4) Retrieverの動作確認。
今回も簡単すぎて全問正解でした。
# Retrieverの動作確認
bm25_retriever.invoke("ギターが得意なのは?")
[Document(page_content='後藤ひとりはギターが上手')]
# Retrieverの動作確認
bm25_retriever.invoke("後藤ふたりの姉は?")
[Document(page_content='後藤ひとりの妹は後藤ふたり')]
# Retrieverの動作確認
bm25_retriever.invoke("伊地知の名前は?")
[Document(page_content='虹夏の名字は伊地知')]
6. ハイブリッド検索
ハイブリッド検索のRetrieverの作成手順は、次のとおりです。
(1) Retrieverの準備
今回は、「EnsembleRetriever」を使います。複数のRetrieverで取得した情報をマージします。weightsでRetrieverの重みを設定します。
from langchain.retrievers import EnsembleRetriever
# Retrieverの準備
ensemble_retriever = EnsembleRetriever(
retrievers=[bm25_retriever, chroma_retriever],
weights=[0.5, 0.5]
)
(2) Retrieverの動作確認。
今回も簡単すぎて全問正解でした。
# Retrieverの動作確認
ensemble_retriever.invoke("ギターが得意なのは?")
[Document(page_content='後藤ひとりはギターが上手')]
# Retrieverの動作確認
ensemble_retriever.invoke("後藤ふたりの姉は?")
[Document(page_content='後藤ひとりの妹は後藤ふたり')]
# Retrieverの動作確認
ensemble_retriever.invoke("伊地知の名前は?")
[Document(page_content='虹夏の名字は伊地知')]
7. RAGの実装
作成したRetrieverを元にRAGを実装します。
(1) PromptTemplateの準備。
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
# PromptTemplateの準備
message = """
提供されたコンテキストのみを使用して、この質問に答えてください。
{question}
Context:
{context}
"""
prompt_template = ChatPromptTemplate.from_messages([("human", message)])
(2) LLMの準備。
from langchain_openai import ChatOpenAI
# LLMの準備
llm = ChatOpenAI(
model="gpt-4o",
)
(3) RAG Chainの準備。
# RAG Chain準備
rag_chain = (
{"context": chroma_retriever, "question": RunnablePassthrough()}
| prompt_template
| llm
)
(4) 質問応答。
# 質問応答
response = rag_chain.invoke("ギターが得意なのは?")
print(response.content)
後藤ひとりがギターが得意です。
# 質問応答
response = rag_chain.invoke("後藤ふたりの姉は?")
print(response.content)
後藤ふたりの姉は後藤ひとりです。
# 質問応答
response = rag_chain.invoke("伊地知の名前は?")
print(response.content)
伊地知の名前は虹夏です。
この記事が気に入ったらサポートをしてみませんか?