2024年2月3日土曜日

LangChain の Retrieval Chains を使って FAISS で検索した類似度情報を元に LLM で回答させるようにしてみる (create_retrieval_chain ver)

LangChain の Retrieval Chains を使って FAISS で検索した類似度情報を元に LLM で回答させるようにしてみる (create_retrieval_chain ver)

概要

LangChain には事前に作成したデータを元に LLM (ChatGPT や AzureOpenAI)を使って回答を作成することができる機能があります
今回は FAISS で作成した類似度検索結果を元に LLM を使って回答を作成する Chains を使う方法を紹介します (これを Retrieval Chain と呼びます

環境

  • macOS 11.7.10
  • Python 3.11.6
  • langchain 0.1.4
    • langchain-openai 0.0.5
    • langchain-community 0.0.17
    • sentence-transformers 2.3.1
    • faiss-cpu 1.7.4
    • beautifulsoup4 4.12.3

サンプルコード

コード中のコメントに詳しい処理の内容を記載しています
ポイントは faiss で作成したベクトル情報を Retrieval に変換し LLM にチェインしている流れになります

from langchain.chains import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import WebBaseLoader
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_community.vectorstores import FAISS
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import AzureChatOpenAI


# AzureOpenAI を LLM として利用する、LLM には最終的に faiss で検索した結果を元に回答を作成してもらう役割
llm = AzureChatOpenAI(
    api_key="xxx",
    api_version="2023-05-15",
    azure_endpoint="https://my-resource.openai.azure.com",
)

# ベクトル化するテキストを Web 上から取得、ここはどんな情報でも OK
# 特定の回答に特化させることができるような情報を使用する
# 今回は WebBaseLoader を使っているが他にも直接テキストを与えたり PDF の情報を与えたりすることができる
loader = WebBaseLoader("https://docs.smith.langchain.com/overview")
docs = loader.load()

# HuggingFaceEmbeddings を使って HuggingFace にあるモデルを使ってテキストをベクトル化する
# ここも Embeddings する際は他の Embeddings 機能でも OK (OpenAI Embeddings など
embeddings = HuggingFaceEmbeddings()
text_splitter = RecursiveCharacterTextSplitter()
documents = text_splitter.split_documents(docs)
vector = FAISS.from_documents(documents, embeddings)

# ベクトル情報の保存と読み込み (一度保存したら二回目以降はロード部分から実行してもOK
vector.save_local("./vectorstore")
vector = FAISS.load_local("./vectorstore", embeddings)

# チャットプロンプトを作成、この {context} の部分に faiss からの結果を与えることで faiss の結果を考慮した回答を作成してもらう
prompt = ChatPromptTemplate.from_template(
    """Answer the following question based only on the provided context:

<context>
{context}
</context>

Question: {input}"""
)

# create_stuff_documents_chain でチャットプロンプトで作成した問い合わせ文言を使って LLM に問い合わせるようにする
document_chain = create_stuff_documents_chain(llm, prompt)

# document_chain を直接使う場合は以下のようにします (今回は更に faiss と連携した結果を使って LLM に問い合わせるのでコメントアウトしています
# from langchain_core.documents import Document
#
# response = document_chain.invoke(
#     {
#         "input": "how can langsmith help with testing?",
#         "context": [
#             Document(page_content="langsmith can let you visualize test results")
#         ],
#     }
# )
# print(response)

# ベクトル化情報を Retrieval に変換します https://python.langchain.com/docs/modules/data_connection/
retriever = vector.as_retriever()

# そしてチャットプロンプト+LLMのチェインに更に Retrieval をチェインすることで faiss の結果を元にチャットプロンプト -> LLM という流れで問い合わせできるようになります
retrieval_chain = create_retrieval_chain(retriever, document_chain)

# 問い合わせ、結果がちゃんと Web から取得した情報を加味した回答になっていることを確認します
response = retrieval_chain.invoke({"input": "how can langsmith help with testing?"})
print(response["answer"])

ちょっと解説

今回は 2 つの Chains を使っています

  • create_stuff_documents_chain
  • create_retrieval_chain

前者は LLM + チャットプロンプトを連携する Chains で LLM への入力にテキストを入力するのではなくテンプレート経由で生成したテキストを入力にしています

後者はテンプレート経由で生成されたテキスト + faiss の連携をしています
faiss で類似度検索結果された情報を元にテンプレートで入力テキストを作成しそれを LLM に投げています

こんな感じで Chains はローカルで実施、加工した情報を元に LLM に質問を投げるといってことができるツールになります (他にもいろいろな連携ができます

最後に

LangChain の Retrival Chain を使って faiss の結果を元に LLM に回答を作成してもらうサンプルを紹介しました
Web 上には同じような手法で RetrievalQA と呼ばれる Chain を使った手法がよく紹介されていますが RetrievalQA は Legacy Chain と呼ばれており古い手法でそのうちなくなるので今回紹介した create_retrieval_chain を使うようにしましょう

LLM をそのままにあたかも専用のモデルを作成したかのような回答を得ることができます
ファインチューニングのようにモデルを専用にビルドする必要がなくマシン側でベクトル環境を準備するだけでカスタムできるのが良い点かなと思います

ただ逆にベクトル化データベースなどの準備必要になるのでそれはデメリットでもあります

参考サイト

0 件のコメント:

コメントを投稿