見出し画像

LangChain で RAGのハイブリッド検索 を試す

「LangChain」でRAGのハイブリッド検索を試したので、まとめました。

・langchain v0.2.0


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)
伊地知の名前は虹夏です。



この記事が気に入ったらサポートをしてみませんか?