独自の前提知識を与えた上でのGPTの回答生成のため、LangChainのRetrievalQAを使用しています。VectorStoreとしてFAISSを使用するときに、FAISSのデータにフィルタをかける方法を記載しておきます。
RAG
今回使用している技術は、RAG(検索拡張生成)と呼ばれます。ChatGPTについて、「RAGとは何ですか?」と質問したところ、以下の回答が返ってきました。
RAG(Retrieval-Augmented Generation)は、自然言語処理(NLP)の分野で使用される、機械学習の技術です。RAGは、大規模な言語モデルと情報検索システムを組み合わせることで、より豊かで正確なテキスト生成を可能にします。
このアプローチでは、まず情報検索システムが、与えられた質問やプロンプトに関連する文書や情報をデータベースから検索します。次に、その検索結果を基にして、言語モデルがより詳細かつ関連性の高い回答を生成します。
RAGは特に、複雑な質問への回答や、広範な知識を必要とするテキスト生成タスクに有効です。この技術は、検索結果に基づいて回答を生成するため、従来の言語モデルよりも正確で豊富な情報を提供することができます。
データ準備
本題に入ります。まず使用するデータの準備です。
以下の推理小説のレビューデータを想定したデータを book_review.csv に保存してあります。
id, title, author, review
1, 謎解き列車, 山田一郎, ページをめくる手が止まらない!
2, 暗闇の謎, 山田一郎, 最後の展開が驚きだった
3, 虚構のアリバイ, 山田一郎, 謎めいた雰囲気が楽しめる
4, 月下の訪問者, 山田一郎, 読む価値あり!面白かった!
5, 迷宮の密室, 山田一郎, 登場人物の心理描写が秀逸
6, 伏線なき追跡, 山田一郎, テンポが良く、飽きさせない
7, 真実を握る探偵, 山田一郎, 謎が深まるにつれ興味が湧いた
8, 疑惑の真実, 山田一郎, 犯人の動機がよく理解できなかった
9, 謎めく逃走者, 山田一郎, 結末が予想外で面白かった
10, 暗闇の謀略, 山田一郎, 解決があまり納得できなかった
11, 虚構の探索, 山田一郎, 謎解きのヒントが分かりにくかった
12, 幻影の調査, 山田一郎, 緊張感が持続して楽しめた
13, 謎めく疑惑, 山田一郎, 登場人物の心の葛藤が魅力的
14, 正体の秘密, 山田一郎, 結末に納得がいかなかった
15, 虚構の追跡, 山田一郎, サスペンス満載の一作
16, 謎めく訪問者, 山田一郎, 読み応えのあるストーリー展開
17, 幻影の陰謀, 山田一郎, シリーズでも楽しめる良作
18, 暗闇の隠れ家, 宮部明日香, 心理戦に魅了された
19, 真実の影, 宮部明日香, 登場人物の心の葛藤が感動的
20, 真夜中の追跡者, 宮部明日香, 読後感がすっきりしない
21, 謎めく訪問者, 宮部明日香, 結末に納得がいかなかった
22, 死者の足跡, 宮部明日香, 読む価値あり!面白かった!
23, 真実の欠片, 宮部明日香, 退屈せずに飽きずに読めた
24, 影法師の探求, 宮部明日香, ラストまで読み応えあり
25, 幻影の逆転, 宮部明日香, 驚きの結末に満足
26, 時間旅行の密室, 高橋伸也, 驚きの連続だった
27, 真実の罠, 高橋伸也, テンポが良く、飽きさせない
28, 螺旋の追想, 高橋伸也, 退屈な展開でした
ベクトルストア作成
book_review.csvをもとに、OpenAIのモデルを利用してFAISSでベクトルストアを作成し、保存します。reviewをベクトル化し、その他の項目をmetadataに設定しています。
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.vectorstores import FAISS
from langchain.schema.document import Document
import pandas as pd
file_path = 'book_review.csv'
faiss_index_path = "./data/faiss_index"
def embedding():
docs = []
df = pd.read_csv(file_path)
for i, row in df.iterrows():
page_content = row['review']
row.pop('review')
metadata = row
docs.append(Document(page_content=page_content, metadata=metadata))
embeddings = OpenAIEmbeddings()
faiss_index = FAISS.from_documents(docs, embeddings)
faiss_index.save_local(faiss_index_path)
if __name__ == '__main__':
embedding()
なお、以降のコードも含め、以下のバージョンで動作確認を行いました。
python:3.9.13
langchain:0.1.1
また、OpenAIのAPIの接続情報は環境変数に設定してあります。
検索拡張生成
保存したベクトルストアを読み込み、faiss_index.as_retrieverのsearch_kwargsにフィルターを設定します。ここでは、author「宮部明日香」という条件指定をして、「驚いた」に近いコメントを出力させます。
from langchain.chat_models import ChatOpenAI
from langchain.chains import RetrievalQA
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.vectorstores import FAISS
faiss_index_path = "./data/faiss_index"
if __name__ == '__main__':
embeddings = OpenAIEmbeddings()
faiss_index = FAISS.load_local(faiss_index_path, embeddings=embeddings)
retriever = faiss_index.as_retriever(
search_type='similarity',
search_kwargs={
'filter': {'author': '宮部明日香'},
},
)
llm = ChatOpenAI(model='gpt-4', temperature=0)
chain = RetrievalQA.from_llm(llm=llm, retriever=retriever)
res = chain.run('推理小説のレビューです。「驚いた」に近いコメントはありますか?')
print(res)
上記のコードの実行結果であるGPTの回答は以下のとおりです。
はい、「驚きの結末に満足」というコメントがその要求に近いと思われます。
「宮部明日香」のreviewを確認すると「驚いた」に近いコメントを回答できています。
id, title, author, review
19, 真実の影, 宮部明日香, 登場人物の心の葛藤が感動的
20, 真夜中の追跡者, 宮部明日香, 読後感がすっきりしない
21, 謎めく訪問者, 宮部明日香, 結末に納得がいかなかった
22, 死者の足跡, 宮部明日香, 読む価値あり!面白かった!
23, 真実の欠片, 宮部明日香, 退屈せずに飽きずに読めた
24, 影法師の探求, 宮部明日香, ラストまで読み応えあり
25, 幻影の逆転, 宮部明日香, 驚きの結末に満足
続いて、以下のようにtitleのフィルタも追加してみます。
retriever = faiss_index.as_retriever(
search_type='similarity',
search_kwargs={
'filter': {'author': '宮部明日香', 'title': '真実の欠片'},
},
GPTの回答は以下のとおりでした。「真実の欠片」のreviewは「退屈せずに飽きずに読めた」ですので、この回答は正しいです。
その情報は提供されていません。
続いて、以下のようにauthorのフィルタ条件指定を変更して、取得データを増やしてみます。
retriever = faiss_index.as_retriever(
search_type='similarity',
search_kwargs={
'filter': {'author': ['宮部明日香', '高橋伸也']},
},
上記のコードの実行結果であるGPTの回答は以下のとおりです。
はい、次のようなコメントがあります。「驚きの結末に満足」、「驚きの連続だった」。これらのコメントは、読者が物語の展開や結末に驚いたことを示しています。
「宮部明日香」と「高橋伸也」のreviewを確認すると正しく回答できています。
id, title, author, review
19, 真実の影, 宮部明日香, 登場人物の心の葛藤が感動的
20, 真夜中の追跡者, 宮部明日香, 読後感がすっきりしない
21, 謎めく訪問者, 宮部明日香, 結末に納得がいかなかった
22, 死者の足跡, 宮部明日香, 読む価値あり!面白かった!
23, 真実の欠片, 宮部明日香, 退屈せずに飽きずに読めた
24, 影法師の探求, 宮部明日香, ラストまで読み応えあり
25, 幻影の逆転, 宮部明日香, 驚きの結末に満足
26, 時間旅行の密室, 高橋伸也, 驚きの連続だった
27, 真実の罠, 高橋伸也, テンポが良く、飽きさせない
28, 螺旋の追想, 高橋伸也, 退屈な展開でした
以上で、FAISSのフィルターを利用した検索拡張生成が実現できました。