見出し画像

LangChainで HyDE による質問応答を試す

「LangChain」で「HyDE」による質問応答を試したので、まとめました。

前回

1. HyDE

「HyDE」(Hypothetical Document Embeddings) は、質問応答に必要な文書の検索をより高い精度で行うための手法です。
一般的な質問応答では、質問を埋め込みにして文書を検索しますが、「HyDE」では文書を読ませず仮想的な応答を生成し、その応答を埋め込みにして文書を検索します。

2. HyDEによる埋め込み生成

Google ColabでのHyDEによる埋め込み生成の手順は、次のとおりです。

(1) パッケージのインストール。

# パッケージのインストール
!pip install langchain
!pip install openai

(2) 環境変数の準備。
以下のコードの <OpenAI_APIのトークン> にはOpenAI APIのトークンを指定します。(有料)

import os
os.environ["OPENAI_API_KEY"] = "<OpenAI_APIのトークン>"

(3) HypotheticalDocumentEmbedderの準備。
引数は、LLMと埋め込みとプロンプトキーです。

from langchain.llms import OpenAI
from langchain.embeddings import OpenAIEmbeddings, HypotheticalDocumentEmbedder
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate

# HypotheticalDocumentEmbedderの準備
embeddings = HypotheticalDocumentEmbedder.from_llm(
    llm=OpenAI(), 
    base_embeddings=OpenAIEmbeddings(), 
    prompt_key="web_search"
)

HyDEには、仮想的な応答を生成するプロンプトがいくつか付属しています。

・web_search
・sci_fact
・arguana
・trec_covid
・fiqa
・dbpedia_entity
・trec_news
・mr_tydi

web_search のプロンプトは、次のとおりです。

web_search_template = """Please write a passage to answer the question 
Question: {QUESTION}
Passage:"""

(4) テキストから埋め込みを生成。

# テキストから埋め込みを生成
result = embeddings.embed_query("Where is the Taj Mahal?")
print(len(result))
print(result)
1536
[-0.00999956764280796, 0.002605215646326542, ...]

3. 複数の仮想的な応答による埋め込み生成

複数の仮想的な応答を生成し、それらの埋め込みを結合することもできます。 デフォルトでは、平均を取って結合します。

(1) OpenAI()の引数 n と best_of を増やす。

# HypotheticalDocumentEmbedderの準備
embeddings = HypotheticalDocumentEmbedder.from_llm(
    llm=OpenAI(n=4, best_of=4), 
    base_embeddings=OpenAIEmbeddings(), 
    prompt_key="web_search"
)

(2) テキストから埋め込みを生成。

# テキストから埋め込みを生成
result = embeddings.embed_query("Where is the Taj Mahal?")
print(len(result))
print(result)
1536
[-0.011480985092930496, -0.004797816043719649, ... ]

4. カスタムプロンプトで仮想的な応答を生成

カスタムプロンプトで仮想的な応答を生成することもできます。
今回は、日本語のプロンプトを作成してみます。

(1) カスタムプロンプトの準備。

# カスタムプロンプトの準備
prompt_template = """質問に回答する文章を書いてください
質問: {question}
回答:"""
prompt = PromptTemplate(input_variables=["question"], template=prompt_template)
llm_chain = LLMChain(llm=OpenAI(), prompt=prompt)

(2) カスタムプロンプトのHypotheticalDocumentEmbedderの準備。
LLMChainを介して設定します。

# カスタムプロンプトのHypotheticalDocumentEmbedderの準備
embeddings = HypotheticalDocumentEmbedder(
    llm_chain=llm_chain, 
    base_embeddings=OpenAIEmbeddings(), 
)

(3) テキストから埋め込みを生成。

# テキストから埋め込みを生成
result = embeddings.embed_query("タージ・マハルはどこにありますか?")
print(len(result))
print(result)
1536
[-0.016797535121440887, 0.00605577789247036, ...]

5. 質問応答

質問応答の手順は、次のとおりです。

(1) ドキュメントの準備。
今回は、マンガペディアの「ぼっち・ざ・ろっく!」の情報からドキュメントを用意しました。

・bocchi.txt

(2) Colabにアップロードしてドキュメントを読み込む。
日本語なので、セパレータを "。" にしています。

from langchain.text_splitter import CharacterTextSplitter
from langchain.vectorstores import FAISS

# ドキュメントの読み込み
with open("bocchi.txt") as f:
    bocchi_txt = f.read()
text_splitter = CharacterTextSplitter(
    chunk_size=500, 
    chunk_overlap=0, 
    separator="。"
)
texts = text_splitter.split_text(bocchi_txt)
print(len(texts))
print(texts)
22
['結束バンド\n後藤ひとりは友達を作れない陰キャでいつも一人で過ごしていたが、...]

(3) 類似文章の検索。
「虹花ちゃんの得意な楽器は?」の質問に対して、虹花ちゃんのプロフィールが検索できてます。

query = "虹花ちゃんの得意な楽器は?"

# 類似文章の検索
docsearch = FAISS.from_texts(texts, embeddings)
docs = docsearch.similarity_search(query, k=1)
print(docs[0].page_content)
...

伊地知虹夏(いじちにじか)
下北沢高校に通う女子。後藤ひとりより1学年上。...

(5) 質問応答の実行。
「ドラム」なので正解です。

# 質問応答の実行
from langchain.chains.question_answering import load_qa_chain
chain = load_qa_chain(OpenAI(), chain_type="stuff")
chain({"input_documents": docs, "question": query}, return_only_outputs=True)
{'output_text': ' ドラム'}

次回



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