Vector Stores(向量存储)

在上一篇文章中,我们介绍了大模型开发框架LangChain的Embedding功能,也就是文本的向量化,今天介绍和向量有关系的另一个重要概念-Vector Stores(向量存储)。 前边我们也学习过LangChain基本文档的查询功能,就是这个系列的第一篇文章: ‘大模型开发轻松上手(1)-使用不同模型回答你的问题,从开发框架LangChain开始’ ,文章里,是我们直接向大模型提问,大模型返回答案。那和我们今天要学习的向量存储有什么不同呢? 这里我们需要说一下公共大模型的一些缺点: 1、因为公共大模型的训练数据是公开的,所以有可能会有一些敏感信息,比如企业内部数据,大模型是不能学习的,自然也不能回答相应的问题。 2、公共大模型的训练数据通常有滞后性,有些新的数据,大模型是不能学习的,自然也不能回答相应的问题。

这时,我们把我们自己的数据提供给大模型,让大模型学习我们的数据,这样大模型就可以回答我们的问题了。这就是向量存储的作用。 直接来看代码示例吧:

 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
29
30
31
32
33
34
35
import os
os.environ["OPENAI_API_BASE"] = "https://api.openai-proxy.org/v1"
os.environ['KMP_DUPLICATE_LIB_OK']='True'
from langchain_community.document_loaders import TextLoader
from langchain_openai import OpenAIEmbeddings
from langchain.text_splitter import RecursiveCharacterTextSplitter

#加载向量存储需要的数据库包
from langchain_community.vectorstores import FAISS

loader = TextLoader("./txtLoad.txt")
docs = loader.load()

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=10, #块长度
    chunk_overlap=10, #重叠字符串长度
    separators=[
        "\u200B",  # 空格
        "\uff0c",  # 全角逗号(,)
        "\u3001",  # 全角顿号(、)
        "\uff0e",  # 全角句号(。)
        "\u3002"  # 全角句号(。)
    ] #对于中文标点,需要写对应的Unicode编码
)
documents = text_splitter.split_documents(docs)

#把Embedding的文档保存到向量存储中
db = FAISS.from_documents(documents, OpenAIEmbeddings(model="text-embedding-3-large"))

query = "介绍一下广州"
#从向量存储中搜索
docs = db.similarity_search(query)
#打印搜索结果
for doc in docs:
    print(f"{doc.page_content}\n-------\n")

如果之前的知识点已经掌握,那么这段代码也很好理解,大部分代码逻辑和之前一样。只是多了一个向量存储的逻辑, 相关代码已经加上了注释,也好理解。 首先,我把txtLoad.txt内容贴出来

 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
29
30
31
32
33
34
35
36
37
38
39
中国的一线城市通常是指在经济、文化、科技、教育、交通等诸多方面发展水平位居全国前列的城市,以下是一些常见的中国一线城市:
北京
政治中心:作为中国的首都,是全国的政治决策中心,众多国家机关、政府部门汇聚于此,承担着国家政治活动的核心职能,各类重要政
策法规从这里制定和发布,对全国的政治生活有着举足轻重的影响。
文化中心:拥有丰富的历史文化资源,故宫、天坛、颐和园等世界文化遗产彰显着古老文明的辉煌。同时,也是现代文化发展的前沿阵地,
汇聚了大量文化艺术机构、博物馆、剧院等,每年举办众多国际文化交流活动,如北京国际电影节、北京国际音乐节等,吸引着世界各地
的文化艺术爱好者。科技创新中心:中关村作为中国科技创新的重要地标,聚集了众多高科技企业和科研机构,如百度、字节跳动等互联
网巨头,以及中科院等顶尖科研单位。在人工智能、大数据、航空航天等领域处于国内领先地位,为国家的科技创新和产业升级提供了强
大动力。
教育资源丰富:拥有北京大学、清华大学等顶尖高校,这些高校在国内外享有极高声誉,培养了大量高素质人才。同时,科研机构众多,
为学术研究和科技创新提供了良好的平台,吸引着全球优秀学子前来求学和科研。
上海
经济中心:是中国的经济中心之一,拥有高度发达的金融、贸易、航运等产业。陆家嘴金融区汇聚了众多国内外金融机构,上海证券交易
所是中国重要的证券交易中心之一。上海港是全球货物吞吐量最大的港口之一,承担着大量的国际贸易运输任务,对全国乃至全球经济的
发展都有着重要影响。
时尚之都:在时尚领域具有很强的影响力,每年举办上海时装周等时尚盛会,吸引了众多国内外知名设计师和品牌参与。同时,也是国际
时尚品牌进入中国市场的重要窗口,众多高端品牌在此设立旗舰店和展示中心,引领着国内时尚潮流。
科技创新高地:在生物医药、集成电路、人工智能等战略性新兴产业领域发展迅速。张江国家自主创新示范区等产业园区汇聚了大量科技
创新企业,吸引了众多高端人才,为上海的科技创新和产业升级提供了有力支撑。
国际化大都市:拥有浓厚的国际化氛围,吸引了大量外籍人士在此工作和生活。城市基础设施完善,交通便利,拥有发达的地铁网络和高
效的公共交通系统。同时,文化多元,汇聚了世界各地的美食、艺术和文化活动,是一个充满活力和魅力的国际化大都市。
广州
商贸中心:是中国重要的商贸中心之一,有着“千年商都”的美誉。广交会是中国历史最长、层次最高、规模最大、商品种类最全、到会客
商最多、成交效果最好的综合性国际贸易盛会之一,每年吸引着大量国内外客商前来参展和采购,促进了中国与世界各国的贸易往来。
制造业重镇:在汽车制造、电子信息、服装纺织等制造业领域具有雄厚的实力。广汽集团等大型汽车制造企业在此扎根,形成了完整的汽车
产业链。同时,电子信息产业也在不断发展,涌现出一批具有竞争力的企业。服装纺织产业更是历史悠久,是中国重要的服装生产基地之一。
交通枢纽:是华南地区的交通枢纽,广州白云国际机场是中国三大航空枢纽之一,航线网络覆盖国内外众多城市。广州南站是中国最大的高
铁站之一,京广高铁、广深港高铁等多条重要铁路干线在此交汇,使广州与全国各地的联系更加紧密。
美食之都:以粤菜闻名于世,粤菜是中国八大菜系之一,以其独特的烹饪技艺和丰富的口味深受人们喜爱。广州的美食种类繁多,从高档
餐厅到街头小吃,都能品尝到正宗的粤菜美食,如广式早茶、烧鹅、肠粉等,吸引着无数美食爱好者前来品尝。
深圳
经济特区:是中国改革开放的窗口和试验田,经济特区的政策优势使其在经济发展方面取得了举世瞩目的成就。作为中国最具经济活力的城市
之一,深圳的GDP总量在全国城市中名列前茅,人均GDP也处于较高水平。
科技创新之城:在科技创新方面具有突出优势,是中国的科技创新中心之一。华为、腾讯等世界500强企业在此诞生和发展壮大,引领着全球
通信、互联网等领域的技术潮流。深圳的高新技术产业园区汇聚了大量创新型企业,形成了良好的创新创业生态,每年专利申请量位居全国前列。
金融中心:深圳证券交易所是中国重要的证券交易所之一,众多上市公司在此挂牌交易。金融产业高度发达,汇聚了大量金融机构和金融人才,
金融创新活跃,在金融科技、风险投资等领域处于国内领先地位。
年轻活力之城:是一座充满年轻活力的城市,吸引了大量年轻人才前来创业和就业。城市氛围开放包容,各种文化活动和艺术展览频繁举办,同时
也有着丰富的夜生活和时尚元素,是一个充满创新精神和活力的现代化大都市。

然后,我把从向量存储中查询出来的结果贴出来

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
。广州的美食种类繁多
-------

广州
商贸中心:是中国重要的商贸中心之一
-------

,使广州与全国各地的联系更加紧密
-------

美食之都:以粤菜闻名于世
------- 

可以看到,确实可以搜索到和我们提问相关的回答。 db.similarity_search(query),这里补充一下similarity_search函数的几个重要参数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#要搜索的文本
query: Text to look up documents similar to.  
#返回的查询到的向量数量,例如,k=2 表示返回最相似的两个向量。注意,这里默认是4。所以上边
#结果返回了4条记录
k: Number of Documents to return. Defaults to 4.
#如果我们给每一条文档设置了metadata,我们可以通过metadata来过滤文档
filter: (Optional[Dict[str, str]]): Filter by metadata. Defaults to None.
#在应用过滤搜索之前,返回多少条向量记录。也就是这时过滤的搜索,会在返回的向量中进行查询。
fetch_k: (Optional[int]) Number of Documents to fetch before filtering.
Defaults to 20.

Retrievers - 检索器

检索器是在给定非结构化查询的情况下返回相关文本的接口。它是比Vector stores更通用的检索接口。 当我们把Vector Stores转变了一个检索器时,我们就可以让Vector Stores适用于更多的场景。 直接来看代码示例吧: 大上边代码基础上,我们加上几行

1
2
3
4
retriever = db.as_retriever()
docs = retriever.invoke("介绍一下上海?")
for doc in docs:
    print(f"{doc.page_content}\n-------\n")

看一下输出结果

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
上海
经济中心:是中国的经济中心之一
-------

。作为中国最具经济活力的城市之一
-------

,每年举办上海时装周等时尚盛会
-------

,是一个充满活力和魅力的国际化大都市
-------

FAISS 索引

在传统数据库查询数据时,索引的重要性不言而喻,在向量存储中,索引也是非常重要的。FAISS是一个用于高效相似性搜索和密集向量聚类的库。 我们看看它的索引功能。 直接来看代码示例吧:

 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
29
30
31
32
33
34
35
36
37
38
39
40
41
import os
os.environ["OPENAI_API_BASE"] = "https://api.openai-proxy.org/v1"
os.environ['KMP_DUPLICATE_LIB_OK']='True'
from langchain_community.document_loaders import TextLoader
from langchain_openai import OpenAIEmbeddings
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import FAISS

loader = TextLoader("./txtLoad.txt")
docs = loader.load()

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=10, #块长度
    chunk_overlap=10, #重叠字符串长度
    separators=[
        "\u200B",  # 空格
        "\uff0c",  # 全角逗号(,)
        "\u3001",  # 全角顿号(、)
        "\uff0e",  # 全角句号(。)
        "\u3002"  # 全角句号(。)
    ] #对于中文标点,需要写对应的Unicode编码
)
documents = text_splitter.split_documents(docs)
embeddings =  OpenAIEmbeddings(model="text-embedding-3-large")
db = FAISS.from_documents(documents, embeddings)
#创建索引,这里的索引保存地址,最好写绝对地址
db.save_local("/Users/liuzhifeng/PycharmProjects/pythonProject/faiss_index") 
#加载索引
new_db = FAISS.load_local(
       "/Users/liuzhifeng/PycharmProjects/pythonProject/faiss_index", 
       embeddings,
       allow_dangerous_deserialization=True)
query = "介绍一下广州"
docs = new_db.similarity_search(query)
for doc in docs:
    print(f"{doc.page_content}\n-------\n")

retriever =new_db.as_retriever()
docs = retriever.invoke("介绍一下上海?")
for doc in docs:
    print(f"{doc.page_content}\n-------\n")

从代码看,索引的创建和加载都是非常简单的,只需要2行代码就可以完成。