使用langchain与你自己的数据对话(二):向量存储与嵌入

news2024/12/23 18:02:30

 

之前我以前完成了“使用langchain与你自己的数据对话(一):文档加载与切割”这篇博客,没有阅读的朋友可以先阅读一下,今天我们来继续讲解deepleaning.AI的在线课程“LangChain: Chat with Your Data”的第三门课:向量存储与嵌入。

Langchain在实现与外部数据对话的功能时需要经历下面的5个阶段,它们分别是:Document Loading->Splitting->Storage->Retrieval->Output,如下图所示:

 在上一篇博客:文档加载与切割中我已经介绍了如何使用Langchain来加载外部的文档,以及如何切割文档,之所以要对文档做加载与切割的操作,是因为外部数据类型和属性有所不同,比如外部数据可能是pdf, text, 网页,youtube视频等,要读取不同类型的外部数据我们就需要有专门的Loader来加载这些数据,所以我们就需要各种类型的文档加载器,当数据被加载器加载以后,接下来我们需要做文档的切割,这是因为外部数据的体量可能比较大,如pdf文档可能会有几十页,几百页的内容,所以我们需要将文档内容按一点尺寸(chunk_size)均匀的切成小块(chunks), 在上一篇博客中我们介绍了几种Langchain常用的文档切割器如RecursiveCharacterTextSplitter, CharacterTextSplitter,TokenTextSplitter,MarkdownHeaderTextSplitter等,其中Langchain默认使用RecursiveCharacterTextSplitter切割器。当文档被切割以后,加下来就到了嵌入(Embeddings)和向量存储(vectorstores)的环节,如下图所示:

 所谓的向量存储是指被切割的文档需要经过向量化操作以后存储到向量数据库的过程,因为大型语言模型(LLM)无法理解文字信息(只能理解数字),所以我们必须对文字信息进行编码,这里说的编码就是只嵌入(Embeddings), 嵌入操作可以将文本转换成数字编码并以向量的形式存储在向量数据库中,如下图所示:

 当文档被切割成块(chunks)后,每一个块都会经嵌入操作后转换成向量并存储在向量数据库中,当用户对文档内容提出问题时,用户的问题也会经嵌入操作后被转换成向量并与向量数据库中的所有向量做相似度比较,最后找出与问题最相关的n个向量,如下图所示:

 当找到与用户问题最相关的n个向量以后,这些向量会被还原成原始文本,然后将用户的问题和这些文本信息发送给LLM, LLM会针对用户的问题对这些文本内容做提炼和汇总,最后给出正确合理的答案,如下图所示:

整个与文档对话的过程大致就是这样,下面我们来实操一下上面的嵌入和向量存储的过程,不过首先我们还是需要做一下些基础性工作,比如设置一下openai的api key:

import os
import openai
import sys
sys.path.append('../..')

from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv()) # read local .env file

openai.api_key  = os.environ['OPENAI_API_KEY']

Document Loading & Splitting

接下来我们首先来实现文档的加载和切割,这里我们会加载一组吴恩达老师著名的机器学习课程cs229的pdf讲义稿:

from langchain.document_loaders import PyPDFLoader

# Load PDF
loaders = [
    # Duplicate documents on purpose - messy data
    PyPDFLoader("docs/cs229_lectures/MachineLearning-Lecture01.pdf"),
    PyPDFLoader("docs/cs229_lectures/MachineLearning-Lecture01.pdf"),
    PyPDFLoader("docs/cs229_lectures/MachineLearning-Lecture02.pdf"),
    PyPDFLoader("docs/cs229_lectures/MachineLearning-Lecture03.pdf")
]
docs = []
for loader in loaders:
    docs.extend(loader.load())

需要说明一下的是这里我们加载了2篇相同的pdf文档:Lecture01.pdf,之所以要加载两篇相同的pdf文档,是为了后面我们需要做一些测试看看当文档内容相同的时候LLM的表现。当文档完成加载以后,下面我们就需要对文档进行切割,首先我们需要创建一个文档切割器RecursiveCharacterTextSplitter:

# Split
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size = 1500,
    chunk_overlap = 150
)

这里关于参数chunk_size ,和chunk_overlap 的含义在文档加载与切割这篇博客中已经详细说明过了,这里不再赘述。当文档切割器创建完成以后,我们可以开始切割文档的操作:

#切割文档
splits = text_splitter.split_documents(docs)

#查看切割后文档的数量
print(len(splits))

 这里我们看到切割后的文档长度是209,也就是说所有的pdf文档被切割成了209块(chunks),我们可以查看其中的某一块的文档内容:

splits[0]

 

 我们看到被切换的文档块中包含了文档的内容(page_content)和元数据(metadata),在元数据中记录了文档的位置和该块内容所在的页数。那么现在在splits中就包含了209个这样的文档块。

Embeddings

所谓的嵌入(Embeddings)是一种文本的编码的方法,它可以一段文字转换成一定长度的一组向量,下面我们来做一下简单的embedding测试:

from langchain.embeddings.openai import OpenAIEmbeddings
embedding = OpenAIEmbeddings()


sentence1 = "我喜欢小狗。"
sentence2 = "我喜欢小动物。"
sentence3 = "我今天心情很差。"

embedding1 = embedding.embed_query(sentence1)
embedding2 = embedding.embed_query(sentence2)
embedding3 = embedding.embed_query(sentence3)

这里我们有三句简单的中文句子,前两句表达人和动物之间的关系,第三句表达人的心情,所以前两句的含义应该比较相似,后第三句和前两句的含义完全不同,下面我们可以通过计算两个向量的点积来得到两个向量的相似度:

np.dot(embedding1, embedding2)

 

np.dot(embedding1, embedding3)

 

np.dot(embedding2, embedding3)

 

 我们可以看到embedding1与embedding2之间有较高的相似性达到了0.94,而embedding3与embedding1和embedding2的相似度只都只有0.8以下,这说明第一句和第二句话有较高的相似度。下面我们看一下经过embedding操作以后的结果是怎么样的:

print(embedding1)

 

 这里我们看到经过embdding操作后生成的向量是一个python的list, 其中包含了很多数字,下面我们再看一下这个embdding的长度:

print(len(embedding1))

 这里我们可以看到经过embdding操作以后生成的向量的长度是1536,也就是说由1536个数字来表示了被embdding的这句文本,我们也可以看成是由1536个维度来表示这句文本。

向量数据库

当我们知道了Embedding的原理以后,接下来我们来介绍一种向量数据库Chroma,Chroma 是开源嵌入(Embedding)数据库。Chroma 通过为大型语言模型(LLM)提供可嵌入的知识、事实和技能,让构建大型语言模型(LLM)的应用程序变得更加容易,如下图所示:

 接下来我们来实际操作创建向量数据库的过程,并且将生成的向量数据库保存在本地。当我们在创建Chroma数据库时,我们需要传递如下参数:

  • documents: 切割好的文档对象
  • embedding: embedding对象
  • persist_directory: 向量数据库存储路径
from langchain.vectorstores import Chroma

#向量数据库保存位置
persist_directory = 'docs/chroma/'

#创建向量数据库
vectordb = Chroma.from_documents(
    documents=splits,
    embedding=embedding,
    persist_directory=persist_directory
)


#查看向量数据库中的文档数量
print(vectordb._collection.count())

 

 这里我们看到向量数据库中存储这209个向量,这和我们之前切割文档后的splits 中的数量是一至的,这说明原来209个文档块已经被转换成了209个向量并且被保存在了Chroma数据库中。

相似度搜索(Similarity Search)

当文档被切割并经embedding操作后转换成向量存储到Chroma数据库中后,我们可以对Chroma数据库中的向量进行相似度的比较,也就是我们可以模拟用户提出问题,然后去Chroma执行相似内容搜索,并返回与问题相似度较高的文本内容:

question = "is there an email i can ask for help"

docs = vectordb.similarity_search(question,k=3)

#打印文档数量
print(len(docs))

这里我们要求向量数据库对问题进行相似度搜索,找出和问题最相关的3个(k=3)文档。下面我们查看其中的一个文档的内容:

docs[0].page_content

 我们看到第一篇文档中包含了"email"这个单词,这和我们的问题显然是相关的。接下来我们来实现向量数据库的持久化:

vectordb.persist()

执行了persist()操作以后向量数据库才真正的被保存到了本地,下次在需要使用该向量数据库时我们只需要从本地加载数据库即可,无需再根据原始文档来生成向量数据库了。

失败的应用场景

虽然有了向量数据库,基本上可以让我们轻松完成 80% 的相似性搜索任务。但也存在一些失败的场景,比如下面的例子:

question = "what did they say about matlab?"

docs = vectordb.similarity_search(question,k=5)

这里我们要求向量数据库搜索5个和问题相关的答案,但是大家还记得之前我们在创建文档加载器时加载了两篇相同的文档(Lecture01.pdf),所以现在向量数据库中应该有重复的向量,因此如果当用户的问题和Lecture01.pdf中的内容相关时,向量数据库会返回重复的内容:

docs[0]

docs[1]

 

 这两我们看到docs[0]和docs[1]的内容是完全一样的,这是因为我们之前加载了重复的文档(Lecture01.pdf)所导致的。如何避免让向量数据库返回重复的内容,我们将在下一篇博客中讨论这个问题,下面我们再看一种失败的场景,这里我们要求向量数据库在第三篇原始文档()中搜索相关答案:

question = "what did they say about regression in the third lecture?"

docs = vectordb.similarity_search(question,k=5)

for doc in docs:
    print(doc.metadata)

从上面的返回结果中我们看到,虽然我们要求向量数据库只能从第三篇文档中搜索相关答案,但是从返回结果的元数据中我们看到第一篇(Lecture01.pdf)和第二篇(Lecture02.pdf)的内容也在其中,这与我们的要求(问题)相违背,因为我们只要求搜索第三篇文档(Lecture03.pdf)即可。这似乎说明向量数据库并没有很好的理解问题的语义。下面我们查看一下返回的最后一个文档的内容(Lecture01.pdf):

print(docs[4].page_content)

 这里我们看到docs[4]对应的是Lecture01.pdf中的第8页的内容,其中也包含了“regression”,这和我们的问题相关。

关于如何避免上述失效的应用场景,我们将会在下一篇博客中进行讨论。

总结

今天我们学习了嵌入和向量数据库的基本原理,并且对嵌入(Embeddings)和开源数据库Chroma进行了实际的操作,并观察了它们的返回结果,同时我们还发现了两种Chroma数据库相似搜索失效的场景。关于如何避免产生失效的结果我们将在下一篇博客中进行讨论。

 参考资料

🏡 Home | Chroma

Chroma | 🦜️🔗 Langchain

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/784471.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java整合chatGPT

public void test_chatGPT() throws IOException {String pro "127.0.0.1";//本机地址int pro1 7890; //代理端口号//创建一个 HttpHost 实例,这样就设置了代理服务器的主机和端口。HttpHost httpHost new HttpHost(pro, pro1);//创建一个 RequestConf…

Appium+python自动化(二十)- Monkey(猴子)日志(超详解)

简介 日志是非常重要的,用于记录系统、软件操作事件的记录文件或文件集合,可分为事件日志和消息日志。具有处理历史数据、诊断问题的追踪以及理解系统、软件的活动等重要作用,在开发或者测试软系统过程中出现了问题,我们首先想到的…

macOS Monterey 12.6.8 (21G725) 正式版发布,ISO、IPSW、PKG 下载

macOS Monterey 12.6.8 (21G725) 正式版发布,ISO、IPSW、PKG 下载 本站下载的 macOS 软件包,既可以拖拽到 Applications(应用程序)下直接安装,也可以制作启动 U 盘安装,或者在虚拟机中启动安装。另外也支持…

Google 登录支付,Firebase 相关设置

登录sdk: https://developers.google.com/identity/sign-in/android/start?hlzh-cn 支付sdk: https://developers.google.com/pay/api/android/overview?hlzh-cn Firebase sdk: https://firebase.google.com/docs/android/setup?hlzh-cn 登录设置: 创建凭据&…

U盘做PE系统盘

下载微PE工具箱 网址:https://www.wepe.com.cn/ 下载好后可以看到一个.exe文件 安装PE系统 先插入U盘到电脑,然后打开微PE工具箱,选择安装到U盘 等待制作成功 这时候U盘会被分成两个分区,EFI分区是PE微系统所在的分区&a…

蓝桥杯专题-真题版含答案-【最大公共子串】【阿姆斯特朗数】【出售金鱼】【亲密数】

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 👉关于作者 专注于Android/Unity和各种游…

Android开发核心技术还是Framework~

这几年,社会问题层出不穷,最头疼的就是民生就业问题,大厂裁员,小厂倒闭,每年大批量的应届毕业生也涌入就业市场。 近几日,统计局也发布了就业相关数据,全国失业青年达600多万,面对此…

Solr原理剖析

一、简介 Solr是一个高性能、基于Lucene的全文检索服务器。Solr对Lucene进行了扩展,提供了比Lucene更为丰富的查询语言,并实现了强大的全文检索功能、高亮显示、动态集群,具有高度的可扩展性。同时从Solr 4.0版本开始,支持SolrCl…

《Ansible自动化工具篇:ubuntu操作系统基于ansible工具一键远程离线部署之K8S1.24.12二进制版集群》

一、部署背景 由于业务系统的特殊性,我们需要针对不同的客户环境部署二进制版K8S集群,由于大都数用户都是专网环境,无法使用外网,为了更便捷,高效的部署,针对业务系统的特性,我这边编写了 基于a…

pytest 核心库 pluggy 详解

目录 前言: 代码案例 实例化: 添加到钩子定义中 (add_hookspecs): 注册插件 (register): 传递实现插件的实体类对象 运行插件 pm.hook.myhook(arg11, arg22):本质就是调用对象的call方法 前言: pytest 是一个广泛使用的自动化测试框架&#xff0c…

明星代言的益处:提升品牌价值与销售增长的有力策略

​在当今竞争激烈的商业世界中,企业为了脱颖而出,吸引更多目标消费者的关注,常常需要运用一系列巧妙的市场营销策略。而其中,明星代言无疑是一种备受瞩目的行之有效的手段。通过让明星成为产品或服务的代言人,企业能够…

freeBSD:(虚拟机和实体机)官网下载——安装

freebsdiso镜像获取 获取 FreeBSD | The FreeBSD Project 官网 根据你 计算机型号找对应的镜像,现在大部分都是amd64的 找寻你需要的镜像,我这里选择4g的镜像,点击即可下载 刻录u盘(虚拟机用户可以跳过) 这里我们使…

C# 存在重复元素

217 存在重复元素 给你一个整数数组 nums 。如果任一值在数组中出现 至少两次 ,返回 true ;如果数组中每个元素互不相同,返回 false 。 示例 1: 输入:nums [1,2,3,1] 输出:true 示例 2: 输…

【100天精通python】Day14:python 标准模块,自定义模块

目录 1 python 模块 2 标准模块 2.1 常见的标准模块 2.2 标准模块的导入 3 自定义模块 3.1 创建模块 3.2 导入模块 3.2.1 使用import 语句导入模块 3.2.2 使用from... import 语句导入模块 3.2.3 导入模块的全部定义 3.2.4 导入两个包含同名函数的模块 模块与函数的…

【Docker】Docker持续集成与持续部署(四)

前言: Docker与持续集成/持续部署(CI/CD)的作用是通过容器化技术实现环境一致性、快速构建和隔离性,从而加快软件交付速度、提高部署效率,确保持续集成和持续部署的顺利实施。 持续集成(Continuous Integra…

chrome解决http自动跳转https问题

1.地址栏输入: chrome://net-internals/#hsts 2.找到底部Delete domain security policies一栏,输入想处理的域名,点击delete。 3.再次访问http域名不再自动跳转https了。

1.项目需求-项目实现

1.项目需求-项目实现代码是依次迭代的 慢慢增加。 密码只能输入整数,密码一般用字符串,用户名和密码都用字符串实现 需要头文件 不识别内容 string需要头文件。c头文件里没有.h #include string 是c特有的类型。 总结:string 是c特有的类型…

【冷冻电镜】InSilicoTEM 使用手册

简介 InSilicoTEM是一个用来模拟生成冷冻电镜显微图像的软件,原论文是"Image formation modeling in cryo-electron microscopy"。 该软件基DIPImage和MATLAB开发。 (注:在GitHub上的代码,只适用于DIPImage2.9以及更早…

C# 父类实现的 IEquatable<T> 接口,重写的相等性判断函数都能被子类继承

实验代码 Son s1 new(), s2 new();Console.WriteLine("_ s1 s2"); _ s1 s2; Console.WriteLine();Console.WriteLine("_ s1 ! s2;"); _ s1 ! s2; Console.WriteLine();Console.WriteLine("_ s1.Equals(s2);"); _ s1.Equals(s2); Cons…

华为数通HCIP-BGP基础

AS(自治系统/路由域) 定义:运行相同路由协议,具有相同管理规则的区域(一般为一个企业网); 按照传递路由时是否携带掩码分类: 1、有类路由协议 路由传递时,无需携带掩…