文末新课《LLM与langchain/知识图谱/数据库的实战》秒杀!!!
前言
过去半年,随着ChatGPT的火爆,直接带火了整个LLM这个方向,然LLM毕竟更多是基于过去的经验数据预训练而来,没法获取最新的知识,以及各企业私有的知识
为了获取最新的知识,ChatGPT plus版集成了bing搜索的功能,有的模型则会调用一个定位于 “链接各种AI模型、工具的langchain”的bing功能
为了处理企业私有的知识,要么基于开源模型微调,要么也可以通过langchain作为一种外挂的内部知识库 (类似存在本地的数据库一样)
所以越来越多的人开始关注langchain并把它与LLM结合起来应用,更直接推动了数据库、知识图谱与LLM的结合应用
本文侧重讲解
LLM与langchain/数据库/知识图谱的结合应用,且解读langchain-ChatGLM项目的关键源码,不只是把它当做一个工具使用,因为对工具的原理更了解,则对工具的使用更顺畅
其中,解读langchain-ChatGLM项目源码其实不易,因为涉及的项目、技术点不少,所以一开始容易绕晕,好在根据该项目的流程一步步抽丝剥茧之后,给大家呈现了清晰的代码架构 过程中,我从接触该langchain-ChatGLM项目到整体源码梳理清晰并写清楚历时了近一周,而大家有了本文之后,可能不到一天便可以理清了(提升近7倍效率),这便是本文的价值和意义之一
阅读过程中若有任何问题,欢迎随时留言,会一一及时回复/解答,共同探讨、共同深挖
第一部分什么是LangChain:LLM的外挂/功能库
1.1 langchain的整体组成架构
通俗讲,所谓langchain (官网地址、GitHub地址),即把AI中常用的很多功能都封装成库,且有调用各种商用模型API、开源模型的接口,支持以下各种组件
初次接触的朋友一看这么多组件可能直接晕了( 封装的东西非常多,感觉它想把LLM所需要用到的功能/工具都封装起来 ),为方便理解,我们可以先从大的层面把整个langchain库划分为三个大层:基础层、能力层、应用层
1.1.1 基础层:models、LLMs、index
Models:模型各种类型的模型和模型集成,比如OpenAI的各个API/GPT-4等等,为各种不同基础模型提供统一接口 比如通过API完成一次问答
LLMS层
这一层主要强调对models层能力的封装以及服务化输出能力,主要有:
各类LLM模型管理平台:强调的模型的种类丰富度以及易用性
一体化服务能力产品:强调开箱即用
差异化能力:比如聚焦于Promp管理(包括提示管理、提示优化和提示序列化)、基于共享资源的模型运行模式等等 比如Google's PaLM Text APIs,再比如 llms/openai.py 文件下
Index:索引
对用户私域文本、图片、PDF等各类文档进行存储和检索(相当于结构化文档,以便让外部数据和模型交互),具体实现上有两个方案:
Vector方案:即对文件先切分为Chunks,在按Chunks分别编码存储并检索,可参考此代码文件:https://github.com/hwchase17/langchain/blob/master/langchain/indexes/vectorstore.py
KG方案:这部分利用LLM抽取文件中的三元组,将其存储为KG供后续检索,可参考此代码文件:https://github.com/hwchase17/langchain/blob/master/langchain/indexes/graph.py
为了索引,便不得不牵涉以下这些能力
document Loaders,文档加载的标准接口 与各种格式的文档及数据源集成,比如 Email、Markdown、PDF (所以可以做类似ChatPDF这样的应用)、Youtube … 相近的还有docstore,其中包含:document_transformersembeddings,涉及到各种embeddings算法,分别体现在各种代码文件中:
elasticsearch.py、google_palm.py、gpt4all.py、huggingface.py、huggingface_hub.py
llamacpp.py、minimax.py、modelscope_hub.py、mosaicml.py
openai.py
sentence_transformer.py、spacy_embeddings.py、tensorflow_hub.py、vertexai.py
1.1.2 能力层:Chains、Memory、Tools
如果基础层提供了最核心的能力,能力层则给这些能力安装上手、脚、脑,让其具有记忆和触发万物的能力,包括:Chains、Memory、Tool三部分
Chains:链接
简言之,相当于包括一系列对各种组件的调用,可能是一个 prompt 模板,一个语言模型,一个输出解析器,一起工作处理用户的输入,生成响应,并处理输出 具体而言,则相当于按照不同的需求抽象并定制化不同的执行逻辑,Chain可以相互嵌套并串行执行,通过这一层,让LLM的能力链接到各行各业 比如与Elasticsearch数据库交互的: elasticsearch_database
比如基于知识图谱问答的:graph_qa其中的代码文件:chains/graph_qa/base.py 便实现了一个基于知识图谱实现的问答系统,具体步骤为:
首先,根据提取到的实体在知识图谱中查找相关的信息「这是通过 self.graph.get_entity_knowledge(entity) 实现的,它返回的是与实体相关的所有信息,形式为三元组」
然后,将所有的三元组组合起来,形成上下文
最后,将问题和上下文一起输入到qa_chain,得到最后的答案 比如能自动生成代码并执行的:llm_math等等 比如面向私域数据的:qa_with_sources,其中的这份代码文件 chains/qa_with_sources/vector_db.py则是使用向量数据库的问题回答
比如面向SQL数据源的:sql_database,可以重点关注这份代码文件:chains/sql_database/base.py
比如面向模型对话的:chat_models,包括这些代码文件:__init__.py、anthropic.py、azure_openai.py、base.py、fake.py、google_palm.py、human.py、jinachat.py、openai.py、promptlayer_openai.py、vertexai.py
另外,还有比较让人眼前一亮的:constitutional_ai:对最终结果进行偏见、合规问题处理的逻辑,保证最终的结果符合价值观llm_checker:能让LLM自动检测自己的输出是否有没有问题的逻辑
Memory:记忆
简言之,用来保存和模型交互时的上下文状态,处理长期记忆 具体而言,这层主要有两个核心点: 对Chains的执行过程中的输入、输出进行记忆并结构化存储,为下一步的交互提供上下文,这部分简单存储在Redis即可 根据交互历史构建知识图谱,根据关联信息给出准确结果,对应的代码文件为:memory/kg.py
Tools层,工具
其实Chains层可以根据LLM + prompt执行一些特定的逻辑,但是如果要用Chain实现所有的逻辑不现实,可以通过Tools层也可以实现,Tools层理解为技能比较合理,典型的比如搜索、Wikipedia、天气预报、ChatGPT服务等等
1.1.3 应用层:Agents
Agents:代理
简言之,有了基础层和能力层,我们可以构建各种各样好玩的,有价值的服务,这里就是Agent 具体而言,Agent 作为代理人去向 LLM 发出请求,然后采取行动,且检查结果直到工作完成,包括LLM无法处理的任务的代理 (例如搜索或计算,类似ChatGPT plus的插件有调用bing和计算器的功能) 比如,Agent 可以使用维基百科查找 Barack Obama 的出生日期,然后使用计算器计算他在 2023 年的年龄
此外,关于Wikipedia可以关注下这个代码文件: langchain/docstore/wikipedia.py ...
最终langchain的整体技术架构可以如下图所示 (查看高清大图,此外,这里还有另一个架构图)
1.2 langchain的部分应用示例:联网搜索 + 文档问答
但看理论介绍,你可能没法理解langchain到底有什么用,为方便大家理解,特举几个langchain的应用示例
1.2.1 通过 Google 搜索并返回答案
由于需要借助 Serpapi 来进行实现,而Serpapi 提供了 Google 搜索的API 接口
故先到 Serpapi 官网(https://serpapi.com/)上注册一个用户,并复制他给我们生成 API key,然后设置到环境变量里面去
然后,开始编写代码
1.2.2 用不到 50 行代码实现一个文档对话机器人
众所周知,由于ChatGPT训练的数据只更新到 2021 年,因此它不知道互联网最新的知识(除非它调用搜索功能bing),而利用 “LangChain + ChatGPT的API” 则可以用不到 50 行的代码然后实现一个和既存文档的对话机器人
假设所有 2022 年更新的内容都存在于2022.txt这个文档中,那么通过如下的代码,就可以让 ChatGPT 来支持回答 2022 年的问题
其中原理也很简单:
对用户的输入/prompt向量化
文档分词
文档分割
文本向量化 向量化了才能进行向量之间相似度的计算
向量化的文本存到向量数据库里
根据用户的输入/prompt去向量数据里寻找答案(答案的判定是基于prompt/输入与文本中相关段落向量的相似性匹配)
最后通过LLM返回答案
第二部分 基于LangChain + ChatGLM-6B的本地知识库问答
2.1 核心步骤:如何通过LangChain+LLM实现本地知识库问答
GitHub上有一个利用 langchain 思想实现的基于本地知识库的问答应用:langchain-ChatGLM (这是其GitHub地址,当然还有和它类似的但现已支持Vicuna-13b的项目,比如LangChain-ChatGLM-Webui ),目标期望建立一套对中文场景与开源模型支持友好、可离线运行的知识库问答解决方案
该项目受 GanymedeNil 的项目 document.ai,和 AlexZhangji 创建的 ChatGLM-6B Pull Request 启发,建立了全流程可使用开源模型实现的本地知识库问答应用。现已支持使用 ChatGLM-6B、 ClueAI/ChatYuan-large-v2 等大语言模型的接入
该项目中 Embedding 默认选用的是 GanymedeNil/text2vec-large-chinese,LLM 默认选用的是 ChatGLM-6B,依托上述模型,本项目可实现全部使用开源模型离线私有部署
本项目实现原理如下图所示 (与基于文档的问答 大同小异,过程包括:1 加载文档 -> 2 读取文档 -> 3/4文档分割 -> 5/6 文本向量化 -> 8/9 问句向量化 -> 10 在文档向量中匹配出与问句向量最相似的top k个 -> 11/12/13 匹配出的文本作为上下文和问题一起添加到prompt中 -> 14/15提交给LLM生成回答 )
第一阶段:加载文件-读取文件-文本分割(Text splitter)
加载文件:这是读取存储在本地的知识库文件的步骤
读取文件:读取加载的文件内容,通常是将其转化为文本格式
文本分割(Text splitter):按照一定的规则(例如段落、句子、词语等)将文本分割,以下只是示例代码(非langchain-ChatGLM项目的源码)
第二阶段:文本向量化(embedding)-存储到向量数据库
文本向量化(embedding):这通常涉及到NLP的特征抽取,可以通过诸如TF-IDF、word2vec、BERT等方法将分割好的文本转化为数值向量
存储到向量数据库:文本向量化之后存储到数据库vectorstore (FAISS,下一节会详解FAISS)
其 中load_k nownlege的实现为
第三阶段:问句向量化
这是将用户的查询或问题转化为向量,应使用与文本向量化相同的方法,以便在相同的空间中进行比较
第四阶段:在文本向量中匹配出与问句向量最相似的top k个
这一步是信息检索的核心,通过计算余弦相似度、欧氏距离等方式,找出与问句向量最接近的文本向量
第五阶段:匹配出的文本作为上下文和问题一起添加到prompt中
这是利用匹配出的文本来形成与问题相关的上下文,用于输入给语言模型
第六阶段:提交给LLM生成回答
最后,将这个问题和上下文一起提交给语言模型(例如GPT系列),让它生成回答 比如知识查询(代码来源)
如你所见,这种通过组合langchain+LLM的方式,特别适合一些垂直领域或大型集团企业搭建通过LLM的智能对话能力搭建企业内部的私有问答系统,也适合个人专门针对一些英文paper进行问答,比如比较火的一个开源项目:ChatPDF,其从文档处理角度来看,实现流程如下(图源):
2.2 Facebook AI Similarity Search(FAISS):高效向量相似度检索
Faiss的全称是Facebook AI Similarity Search (官方介绍页、GitHub地址),是FaceBook的AI团队针对大规模相似度检索问题开发的一个工具,使用C++编写,有python接口,对10亿量级的索引可以做到毫秒级检索的性能
简单来说,Faiss的工作,就是把我们自己的候选向量集封装成一个index数据库,它可以加速我们检索相似向量TopK的过程,其中有些索引还支持GPU构建
2.2.1 Faiss检索相似向量TopK的基本流程
Faiss检索相似向量TopK的工程基本都能分为三步:
1、得到向量库
2、用faiss 构建index,并将向量添加到index中
其中的构建索引选用暴力检索的方法FlatL2,L2代表构建的index采用的相似度度量方法为L2范数,即欧氏距离
3、用faiss index 检索,检索出TopK的相似query
打印输出为:
[[ 0 393 363 78]
[ 1 555 277 364]
[ 2 304 101 13]
[ 3 173 18 182]
[ 4 288 370 531]]
[[ 0. 7.17517328 7.2076292 7.25116253]
[ 0. 6.32356453 6.6845808 6.79994535]
[ 0. 5.79640865 6.39173603 7.28151226]
[ 0. 7.27790546 7.52798653 7.66284657]
[ 0. 6.76380348 7.29512024 7.36881447]]
2.2.2 FAISS构建索引的多种方式
构建index方法和传参方法可以为
dim为向量维数
最重要的是param参数,它是传入index的参数,代表需要构建什么类型的索引;
measure为度量方法,目前支持两种,欧氏距离和inner product,即内积。因此,要计算余弦相似度,只需要将vecs归一化后,使用内积度量即可
此文,现在faiss官方支持八种度量方式,分别是:
METRIC_INNER_PRODUCT(内积)
METRIC_L1(曼哈顿距离)
METRIC_L2(欧氏距离)
METRIC_Linf(无穷范数)
METRIC_Lp(p范数)
METRIC_BrayCurtis(BC相异度)
METRIC_Canberra(兰氏距离/堪培拉距离)
METRIC_JensenShannon(JS散度)
2.2.2.1 Flat :暴力检索
优点:该方法是Faiss所有index中最准确的,召回率最高的方法,没有之一;
缺点:速度慢,占内存大。
使用情况:向量候选集很少,在50万以内,并且内存不紧张。
注:虽然都是暴力检索,faiss的暴力检索速度比一般程序猿自己写的暴力检索要快上不少,所以并不代表其无用武之地,建议有暴力检索需求的同学还是用下faiss。
构建方法:
2.2.2.2 IVFx Flat :倒排暴力检索
优点:IVF主要利用倒排的思想,在文档检索场景下的倒排技术是指,一个kw后面挂上很多个包含该词的doc,由于kw数量远远小于doc,因此会大大减少了检索的时间。在向量中如何使用倒排呢?可以拿出每个聚类中心下的向量ID,每个中心ID后面挂上一堆非中心向量,每次查询向量的时候找到最近的几个中心ID,分别搜索这几个中心下的非中心向量。通过减小搜索范围,提升搜索效率。
缺点:速度也还不是很快。
使用情况:相比Flat会大大增加检索的速度,建议百万级别向量可以使用。
参数:IVFx中的x是k-means聚类中心的个数
构建方法:
2.2.2.3 PQx :乘积量化
优点:利用乘积量化的方法,改进了普通检索,将一个向量的维度切成x段,每段分别进行检索,每段向量的检索结果取交集后得出最后的TopK。因此速度很快,而且占用内存较小,召回率也相对较高。
缺点:召回率相较于暴力检索,下降较多。
使用情况:内存及其稀缺,并且需要较快的检索速度,不那么在意召回率
参数:PQx中的x为将向量切分的段数,因此,x需要能被向量维度整除,且x越大,切分越细致,时间复杂度越高
构建方法:
2.2.2.4 IVFxPQy 倒排乘积量化
优点:工业界大量使用此方法,各项指标都均可以接受,利用乘积量化的方法,改进了IVF的k-means,将一个向量的维度切成x段,每段分别进行k-means再检索。
缺点:集百家之长,自然也集百家之短
使用情况:一般来说,各方面没啥特殊的极端要求的话,最推荐使用该方法!
参数:IVFx,PQy,其中的x和y同上
构建方法:
2.2.2.5LSH 局部敏感哈希
原理:哈希对大家再熟悉不过,向量也可以采用哈希来加速查找,我们这里说的哈希指的是局部敏感哈希(Locality Sensitive Hashing,LSH),不同于传统哈希尽量不产生碰撞,局部敏感哈希依赖碰撞来查找近邻。高维空间的两点若距离很近,那么设计一种哈希函数对这两点进行哈希计算后分桶,使得他们哈希分桶值有很大的概率是一样的,若两点之间的距离较远,则他们哈希分桶值相同的概率会很小。
优点:训练非常快,支持分批导入,index占内存很小,检索也比较快
缺点:召回率非常拉垮。
使用情况:候选向量库非常大,离线检索,内存资源比较稀缺的情况
构建方法:
2.2.2.6 HNSWx
优点:该方法为基于图检索的改进方法,检索速度极快,10亿级别秒出检索结果,而且召回率几乎可以媲美Flat,最高能达到惊人的97%。检索的时间复杂度为loglogn,几乎可以无视候选向量的量级了。并且支持分批导入,极其适合线上任务,毫秒级别体验。
缺点:构建索引极慢,占用内存极大(是Faiss中最大的,大于原向量占用的内存大小)
参数:HNSWx中的x为构建图时每个点最多连接多少个节点,x越大,构图越复杂,查询越精确,当然构建index时间也就越慢,x取4~64中的任何一个整数。
使用情况:不在乎内存,并且有充裕的时间来构建index
构建方法:
2.3 项目部署:langchain + ChatGLM-6B搭建本地知识库问答
2.3.1 部署过程一:支持多种使用模式
其中的LLM模型可以根据实际业务的需求选定,本项目中用的ChatGLM-6B,其GitHub地址为:https://github.com/THUDM/ChatGLM-6B
ChatGLM-6B 是⼀个开源的、⽀持中英双语的对话语⾔模型,基于 General LanguageModel (GLM) 架构,具有 62 亿参数。结合模型量化技术,用户可以在消费级的显卡上进行本地部署(INT4 量化级别下最低只需 6GB 显存)
ChatGLM-6B 使用了和 ChatGPT 相似的技术,针对中文问答和对话进行了优化。经过约 1T 标识符的中英双语训练,辅以监督微调、反馈自助、人类反馈强化学习等技术的加持,62 亿参数的 ChatGLM-6B 已经能生成相当符合人类偏好的回答
1.新建一个python3.8.13的环境(模型文件还是可以用的)
2.拉取项目
3.进入目录
4.安装requirements.txt
5.当前环境支持装langchain的最高版本是0.0.166,无法安装0.0.174,就先装下0.0.166试下 修改配置文件路径:
6.将chatglm-6b的路径设置成自己的
7.修改要运行的代码文件:webui.py
8.将最后launch函数中的share设置为True,inbrowser设置为True
9.执行webui.py文件
可能是网络问题,无法创建一个公用链接。 可以进行云服务器和本地端口的映射, 参考: https://www.cnblogs.com/monologuesmw/p/14465117.html
对应输出:
占用显存情况:大约15个G
2.3.2 部署过程二:支持多种社区上的在线体验
项目地址:https://github.com/thomas-yanxin/LangChain-ChatGLM-Webui
HUggingFace社区在线体验:https://huggingface.co/spaces/thomas-yanxin/LangChain-ChatLLM
另外也支持ModelScope魔搭社区、飞桨AIStudio社区等在线体验
1.下载项目
2.进入目录
3.安装所需的包
5. 修改config.py
5.修改app.py文件,将launch函数中的share设置为True,inbrowser设置为True 执行webui.py文件
显存占用约13G
新课上线
七月在线新课《LLM与langchain/知识图谱/数据库的实战》上线了
开课时间:23年9月9日(报名后进群开始预习、答疑),每周六晚上8点半
↓↓↓扫码抢购↓↓↓
↓↓↓扫码抢购↓↓↓
《LLM与langchain/知识图谱/数据库的实战》
开课时间:23年9.9开班(群内早已开始预习和答疑),每周六晚上8点半