1. 导入 pdf
这里使用 PyPDF2 来读取 pdf, 而不使用 from langchain.document_loaders import PyPDFLoader
. 因为实际中需要定制 pdf 来对表格图片识别和整理。然后在
from PyPDF2 import PdfReader
file_path = r"./2021 新青年居住消费报告贝壳研究院.pdf"
pdf_reader = PdfReader(file_path)
docs = []
for i, page in enumerate(pdf_reader.pages):
content = page.extract_text()
if content and i > 1:
# print("第", i, content, end='\n')
# text += content
docs.append(content)
from langchain.docstore.document import Document
docs = [Document(page_content=doc) for doc in docs]
docs
这里是输出的内容:
[Document(page_content='引言 \n 贝壳研究院于 2020 年发布《新青年住房趋势报告》后,引发了社会广泛关注,其中,话题“租 \n 房压力最大的 10 大城市”一度登上微博热搜。对于新青年,他们的居住消费行为有什么特征?又表现出怎样的趋势?独居新青年的居住消费态度又表现出什么样的特征...... 报告对这些问题进行了详细解答,以展现出新青年的居住消费特征。该报告的意义体现在哪里?对于新青年而言,帮助其了解群体的居住特征,为置业选择提供可行性建议;对于房企而言,新青年已然成为了主力置业人群,洞察该群体的居住痛点,有助于产品的研发设计;对于政府而言,“尽最大努力帮助新市民、青年人等缓解住房困难”是来自政府工作报告的庄严承诺,对于该群体的置业意愿与需求的研究,为宏观政策的制定提供参考依据。继上版报告发布后,值 2021 年五四青年节之际,贝壳研究院发布《2021 年新青年居住消费报告》,除延续上版报告部分分析维度之外,进行了部分调整和延展。需要说明的是,本版报告对于新青年的界定范围进行了扩展,从 18-30 周岁扩展到 18-35 周岁。以《中长期青年发展规划(2016-2025)》为依据,青年的年龄范围是 14-35 周岁,考虑到居住消费低频、高额度及家庭单位等消费特征,贝壳研究院将年龄下限提升到 18 周岁,即成年人的法定界限。那么,何谓新青年?新京报官方微博于 2019 年 9 月发起过你心中新青年的标准是什么的话题,答案莫衷一是,我们既可以从思想状态进行界定,新青年应该是目标明确,积极向上,努力进取的群体;也可从生理状态上进行界定,新青年应该是健康强壮的群体。无论是从心理状态,还是从生理状态,18-35 周岁年龄段人群是符合这一界定的。本版报告做了调整,将从新青年群体居住行为,居住负担及独居现状进行阐述,主要采用问卷调查的定量研究方法,同时结合贝壳找房大数据平台的真实成交数据,进行了指标建模。目的是通过多维度分析,进一步分析新青年群体的居住行为特征,消费痛点及需求变化情况,特别是当前独居趋势明显的社会背景下,新青年群体的居住消费观表现出的特征,如租买意愿,居住空间偏好等。将新青年群体画像刻画得更为深刻,为房企产品开发、政府的政策制定提供有价值的参考。'),
...
Document(page_content='关于贝壳研究院 \n 贝壳研究院是隶属于贝壳找房的研究机构,依托于贝壳交易平台的海量真实交易数据、丰富的房屋交易场景和多元的产品,围绕新居住产业互联网的前沿问题,致力于成为新居住领域的领先智库。通过开放合作的研究平台,贝壳研究院与国内国外研究机构、智库开展多元化合作,围绕市场与行业政策、居住产业互联网、大数据与科技创新等研究方向,为政府部门、行业决策者提供专业、深度的学术研究,为产业发展和政策制定提供研究支持,也为社会公众提供通俗易懂、内容丰富的市场信息和趋势洞察。如欲了解更多有关贝壳研究院的详细资料,请发送邮件至:keyanjiuyuan@ke.com 如欲了解更多贝壳研究院的其他研究成果与洞察,请关注贝壳研究院的官方网站:https://re-search.ke.com/;或关于贝壳研究院官方微信账号:贝壳研究院,ID:beikeyanjiuyuan')]
2. 对文档进行切分
主要是 3 步:
- 切分
CharacterTextSplitter
- 建立 embedding,并使用向量数据库存储
- 搜索得到跟问题相似的 topk embedding
import os
open_api_key="sk-xx"
os.environ["OPENAI_API_KEY"]=open_api_key
from langchain import PromptTemplate
from langchain.chat_models import ChatOpenAI
from langchain.chains.summarize import load_summarize_chain
from langchain.docstore.document import Document
from langchain.text_splitter import CharacterTextSplitter
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.vectorstores import Chroma
llm = ChatOpenAI(temperature=0, model_name='gpt-3.5-turbo')
#1. 切分
text_splitter = CharacterTextSplitter(chunk_size=4000, chunk_overlap=100)
texts = text_splitter.split_documents(docs)
#2.embedding
embeddings = OpenAIEmbeddings()
doc_search = (Chroma.from_documents(texts,
embeddings,
)
.as_retriever(search_kwargs={"k": 2})) #定义搜索 top-k, api 限制定义小一点的
#3. 搜索
query = "哪些城市租赁压力大?"
top_k_docs = doc_search.get_relevant_documents(query)
print(top_k_docs)
找到的 top 2 相似的文档如下:
[Document(page_content='132.2 租赁分析 \n2.2. 1 租赁舒适区间:哪些城市租赁压力更大?\n 城市 房租收入比 最低舒适线 最高舒适线 舒适区间判断 \n 北京 38.5 1207 3620 过高 \n 上海 35.7 1170 3510 过高 \n 深圳 34.4 1098 3295 过高 \n 杭州 28.2 1018 3053 匹配 \n 南京 24.4 951 2853 匹配 \n 广州 23.6 962 2887 匹配 \n 珠海 22.2 921 2764 匹配 \n 苏州 21 907 2720 匹配 \n 厦门 18.3 902 2708 匹配 \n 青岛 19.9 817 2452 匹配 \n 成都 18.7 868 2602 匹配 \n 大连 22 732 2197 匹配 \n 武汉 18.5 867 2600 匹配 \n 西安 19.4 807 2422 匹配 \n 南宁 18.9 829 2487 匹配 \n 宁波 17.1 904 2711 匹配 \n 天津 19.2 803 2409 匹配 \n 合肥 18.2 843 2528 匹配 \n 济南 18 836 2509 匹配 \n 无锡 18 826 2478 匹配 \n 东莞 15.7 919 2758 匹配 \n 长沙 16.7 859 2576 匹配 \n 福州 17.2 830 2490 匹配 \n 长春 19.9 682 2046 匹配 \n 郑州 17 794 2381 匹配 \n 重庆 15.6 859 2578 匹配 \n 南昌 15.8 829 2487 匹配 \n 昆明 16.4 798 2393 匹配 \n 佛山 14.4 890 2670 匹配 \n 沈阳 18.2 689 2067 匹配 \n 贵阳 15.1 812 2437 匹配 \n 太原 17.3 709 2128 匹配 \n 烟台 16.9 708 2123 匹配 \n 哈尔滨 17.2 685 2054 匹配 \n 石家庄 16 731 2194 匹配表 新青年样本城市租赁舒适区间分布情况'), Document(page_content='132.2 租赁分析 \n2.2. 1 租赁舒适区间:哪些城市租赁压力更大?\n 城市 房租收入比 最低舒适线 最高舒适线 舒适区间判断 \n 北京 38.5 1207 3620 过高 \n 上海 35.7 1170 3510 过高 \n 深圳 34.4 1098 3295 过高 \n 杭州 28.2 1018 3053 匹配 \n 南京 24.4 951 2853 匹配 \n 广州 23.6 962 2887 匹配 \n 珠海 22.2 921 2764 匹配 \n 苏州 21 907 2720 匹配 \n 厦门 18.3 902 2708 匹配 \n 青岛 19.9 817 2452 匹配 \n 成都 18.7 868 2602 匹配 \n 大连 22 732 2197 匹配 \n 武汉 18.5 867 2600 匹配 \n 西安 19.4 807 2422 匹配 \n 南宁 18.9 829 2487 匹配 \n 宁波 17.1 904 2711 匹配 \n 天津 19.2 803 2409 匹配 \n 合肥 18.2 843 2528 匹配 \n 济南 18 836 2509 匹配 \n 无锡 18 826 2478 匹配 \n 东莞 15.7 919 2758 匹配 \n 长沙 16.7 859 2576 匹配 \n 福州 17.2 830 2490 匹配 \n 长春 19.9 682 2046 匹配 \n 郑州 17 794 2381 匹配 \n 重庆 15.6 859 2578 匹配 \n 南昌 15.8 829 2487 匹配 \n 昆明 16.4 798 2393 匹配 \n 佛山 14.4 890 2670 匹配 \n 沈阳 18.2 689 2067 匹配 \n 贵阳 15.1 812 2437 匹配 \n 太原 17.3 709 2128 匹配 \n 烟台 16.9 708 2123 匹配 \n 哈尔滨 17.2 685 2054 匹配 \n 石家庄 16 731 2194 匹配表 新青年样本城市租赁舒适区间分布情况')]
3. 将上下文和问题一起输入到 llm 生成回答
from langchain.chains.question_answering import load_qa_chain
chain = load_qa_chain(llm=llm, chain_type='map_reduce')
chain.run(input_documents=top_k_docs, question=query)
答案是 '北京、上海、深圳的租赁压力较大。'
这跟文档内容也是相符合。当然,这个问题非常简单,pdf 抽取也非常好,因为 openai api 限制的问题。就不慢慢尝试了。以后会使用本地的大模型来比较。另外,不同的 chain_type 选择也是重要的,chain_type 类型解释 。
map_reduce
: 每个单独的文档(Map 步骤),将链的输出视为新文档。然后,将所有新文档传递给单独的合并文档链以获得单一输出(Reduce 步骤)。在执行 Map 步骤前也可以对每个单独文档进行压缩或合并映射,以确保它们适合合并文档链;可以将这个步骤递归执行直到满足要求stuff
: 获取一个文档列表,带入提示上下文,传递给 LLM(适合小文档)refine
: 在 Studff 方式上进一步优化,循环输入文档并迭代更新其答案,以获得最好的最终结果。具体做法是将所有非文档输入、当前文档和最新的中间答案组合传递给 LLM。
参考
正文完