LLM 系列 | 15:如何用LangChain做长文档问答?

简介
西塞山前白鹭飞 , 桃花流水鳜鱼肥 。
小伙伴们好 , 我是微信公众号《小窗幽记机器学习》的小编:卖酱猪蹄的小女孩 。今天新开一个专题:实践 。前文工程和应用系列文章可以如下自取 , 预告一下该系列还有2篇小作文 , 后续补下 。
本文作为专题的开篇 , 以长文档问答为例介绍如何使用 。完整代码请在微信公众号:小窗幽记机器学习上添加小编微信 。
加载数据
【LLM 系列 | 15:如何用LangChain做长文档问答?】本次测试数据来源于百度百科上画江湖之不良人的词条数据 , 并以pdf格式保存到本地(右键->打印->另存为pdf) 。
# load documentfrom langchain.document_loaders import PyPDFLoaderloader = PyPDFLoader("test_data.pdf")documents = loader.load()
补充说下 , 如果是多个文档可以如下类似方式进行加载:
loaders = [....]documents = []for loader in loaders:documents.extend(loader.load())
短文本问答
是中最通用的问答接口 , 用户可以对输入的文档进行问答 。需要注意:默认使用文档中的全部文本 。如果将整个文档全部输入的话 , 可能会报错 , 所以本章节先只将带有答案的文档页面输入 , 后文补充介绍如何做整个文档的问答 。
text--003
以下使用text--003模型(该模型是接口的默认模型)进行问答:

LLM 系列 | 15:如何用LangChain做长文档问答?

文章插图
from langchain.chains.question_answering import load_qa_chainllm = OpenAI() # model_name="text-davinci-003"chain = load_qa_chain(llm=llm)query = "李星云会哪些武功?"chain.run(input_documents=documents[1:2], question=query)
输出结果如下:
' Li Xingyun can perform the following martial arts techniques: Qinglian Sword Song, Huayang Needle Method, and Longquan Seven Star Scripture.'
可以看出 , 回答的内容是正确的 。如果想要以中文形式回答 , 可以通过设计作为额外输入 。
改用模型进行问答:
from langchain.chains.question_answering import load_qa_chainmodel_name = "gpt-3.5-turbo"llm = OpenAI(model_name=model_name, temperature=0)chain = load_qa_chain(llm=llm)query = "李星云会哪些武功?"chain.run(input_documents=documents[1:2], question=query)
输出结果如下:
'李星云的武功包括青莲剑歌、华阳针法、龙泉七星诀 。'
与原文查找确认相同:
长文档问答
以上通过人工指定候选文本范围 , 但真实场景我们希望基于全部文档自动搜索答案 。
向量召回
为此 , 改变思路先进行召回 , 缩小文档范围:
from langchain.embeddings.openai import OpenAIEmbeddingsfrom langchain.text_splitter import CharacterTextSplitterfrom langchain.vectorstores import Chromatext_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)texts = text_splitter.split_documents(documents)embeddings = OpenAIEmbeddings() # 默认的model="text-embedding-ada-002"docsearch = Chroma.from_documents(texts, embeddings, metadatas=[{"source": str(i)} for i in range(len(texts))]).as_retriever()# 可以在 as_retriever 指定参数 search_kwargs={"k": 1} , 从而实现Top K的召回query = "李星云会哪些武功?"docs = docsearch.get_relevant_documents(query)print("docs len=", len(docs))
为此 , 召回了4个docs片段 。
text--003
from langchain.chains.question_answering import load_qa_chainllm = OpenAI(temperature=0)chain = load_qa_chain(llm=llm, chain_type="map_reduce")query = "李星云会哪些武功?"chain.run(input_documents=docs, question=query)