热门搜索:和平精英 原神 街篮2 

您的位置:首页 > > 教程攻略 > ai资讯 >一步步指导您发现和利用矢量数据库的强大功能

一步步指导您发现和利用矢量数据库的强大功能

来源:互联网 更新时间:2026-08-26 21:36

向量数据库现在是炙手可热的话题,大公司纷纷砸钱开发专用向量数据库,或者给现有的关系型、非关系型数据库加上向量搜索功能。这篇文章会带你从原理到动手,完整走一遍。

目录

简介
矢量数据库有什么特别之处?
我们如何将句子的含义映射到数字表示?
这对我们的 LLM 应用程序有何帮助?
为什么我们不能将我们拥有的所有数据都提供给 LLM?
实践教程——文本到嵌入和距离度量
1. 文本到嵌入
2. 使用 PCA 在 2 中绘制 384 个维度
3. 计算距离度量
走向向量存储
如何加速相似性搜索?
有哪些不同的向量存储可供选择?
动手教程——设置你的第一个矢量商店
1. 安装 chroma
2. 获取/创建 chroma 客户端和集合
3. 将一些文本文档添加到集合中
4. 从数据库提取所有条目到 excel 文件
5. 查询集合
摘要

矢量数据库有什么特别之处?

向量数据库的核心能力,就是快速搜索和比较海量向量。这之所以引人注目,是因为今天的嵌入模型已经能很好地理解词语背后的语义,并将其转换成向量。这意味着我们可以有效地让句子之间做比较。

那么,我们为什么要在意这件事?

对于大多数大语言模型(LLM)应用来说,我们依赖这个功能,因为 LLM 本身的信息是有边界的——它只能看到训练集那个时刻的世界,之后发生的事或私有数据它一概不知。

所以我们需要在运行时给模型提供它自己不可能知道的额外数据。这就得有一个流程,能快速决定到底该喂哪些数据给模型。

如果用传统的关键字搜索,会遇到两个硬伤:

  • 语言本身很复杂。同一件事用 20 种说法都行,单纯搜关键词往往不够用。我们需要一种方法来捕捉词句背后的含义,找到真正相关的内容。
  • 搜索必须在几毫秒内完成,不能拖到几秒甚至几分钟。所以我们需要高效的搜索步骤,在向量集合里快速查找。

首先,我们如何将句子的含义映射到数字表示?

在搜索数据库之前,得先把文本内容转成能捕捉语义的向量。OpenAI、Google、MetaAI 以及开源社区的预训练嵌入模型都能帮上忙。它们从海量文本中学会了词语的常见用法和上下文,然后利用这些知识把词语映射到多维向量空间里——空间里新点的位置,告诉我们哪些词是相关的。

举个简单的例子:假设我们把各种水果和蔬菜的“含义”放到一个二维向量空间里。如果嵌入模型靠谱,苹果和梨应该比苹果和洋葱离得更近(至少凭常识是这样)。相似性受很多特征影响,比如大小、颜色、味道、产地等,完全看你描述的对象是什么。

嵌入模型的学习方式和人类学语言很像——通过观察词语在上下文中如何被使用。你小时候听对话、读书,慢慢就懂了词的意思。模型训练过程也差不多,它发现“梨和苹果”在句子里一起出现的概率比“苹果和洋葱”高,于是推测它们有共同之处。那么对于食物来说,最重要的可能就是是否常一起食用。

物体、词句的特征太多,二维根本不够用。所以现代嵌入模型会把词句转换成几百甚至几千维的向量。

有了向量,就能测量它们之间的距离了。二维例子很容易,但维度一上去,计算量就大了——这正是向量数据库大显身手的地方。

它们用了多种技术来高效存储和搜索文本集合。一旦有了向量集合,我们要做的就是把它们互相比较,量化相似度。通常我们关心 k-最近邻——向量空间里离查询向量最近的那些点。下面这个例子,查询是单词"apple",不过用的是嵌入模型返回的向量,而不是单词本身。

这对我们的 LLM 应用程序有何帮助?

当 LLM 自己的知识不够用时,很多应用都会采用向量搜索的方法。
LLM 不知道的东西大致分两类:

  • 太新的数据——有关时事、最新创新等,训练集之后才出现的内容。
  • 非公开数据——个人数据、公司内部数据、商业秘密等。

为什么我们不能把我们拥有的所有数据都提供给模型?

简短回答:模型有 token 限制。

如果我们不想训练或微调模型,那就只能把必要信息塞进提示里。但提示的长度必须尊重模型的 token 上限。

LLM 的 token 限制是出于实际和技术原因。OpenAI 最新的模型大约 4,000–32,000 个 token,开源 LLM 如 Llama 默认只有 2,048 个(不微调的情况下)。微调可以增大最大 token 数,但数据越多不一定越好。32,000 个 token 足以一次把长文本放进提示里,但这么做是否合适应另当别论。(Lample, 2023; OpenAI, 2023)

数据的质量远比数量重要,不相关的数据反而会拉低结果。甚至提示里信息的排列顺序都会影响 LLM 理解的准确性。斯坦福大学的研究发现,当重要信息放在提示开头或结尾时,答案通常更准确;放在中间,准确率可能显著下降。所以喂什么数据、怎么构造提示,都得仔细斟酌。(Liu 等, 2023; Raschka, 2023)

输入数据的质量是 LLM 应用成功的关键,必须有一个流程能精准识别相关内容,避免塞进太多没用的数据。为了保证这一点,我们需要用有效的搜索方法把最相关的信息找出来。

向量数据库究竟如何帮助我们做到这一点?

向量数据库由几个关键组件构成,能帮我们快速找到想要的东西。其中索引是最重要的部分,在插入新数据时只需要执行一次,之后搜索就快多了。

要把数据放进向量数据库,首先得把所有内容转成向量。就像本文第一部分说的,我们可以用嵌入模型来做这件事。为了方便,通常直接用 OpenAI、Google 等提供的现成服务。

下面这张图列出了一些可以选的嵌入模型。

动手教程——文本转矢量

要用这个示例,你需要 Hugging Face API 和句子转换器模型"all-MiniLM-L6-v2"。先去 https://huggingface.co/settings/tokens 拿你的令牌。

下面的代码片段做了三件事:

  1. 把文本片段转成 384 维的向量(维度取决于嵌入模型),从而在向量里捕获句子的含义。
  2. 通过计算句子之间的距离,识别数据点之间的相似性。
  3. 为了在简单的二维图上直观展示,我们使用主成分分析(PCA)把 384 维降到两维。这可能会丢失大量信息,但值得试试。

我生成了 10 个随机示例句子:

text_chunks = [ 
    "天空是蓝色的。",
    "草是绿色的。", 
    "太阳在闪耀。",
    "我喜欢巧克力。",
    "披萨很好吃。",
    "编码很有趣。",
    "玫瑰是红色的。",
    "紫罗兰是蓝色的。",
    "水是生命的必需品。",
    "月球绕地球运行。",
]

如果你想亲自跟着做,我已经把步骤拆成了独立的数据管道。

先用下面的命令创建一个新的虚拟环境 .venv:

安装"requirements.txt"里的所有模块:

pip install -r requirements.txt
anyio==4.0.0 backoff==2.2.1 
bcrypt==4.0.1 certifi==2023.7.22 
charset-normalizer==3.2.0 chroma-hnswlib==0.7.3 
chromadb==0.4.10 click==8.1.7 
colorama==0.4.6 coloredlogs==15.0.1 
contourpy==1.1.1 cycler==0.11.0 
exceptiongroup==1.1.3 fastapi==0.99.1 
filelock==3.12.4 flatbuffers==23.5.26 
fonttools==4.42.1 fsspec==2023.9.1 
h11==0.14.0 httptools==0.6.0 
huggingface-hub==0.16.4 humanfriendly==10.0 
idna==3.4 importlib-resources==6.0.1 
joblib==1.3.2 kiwisolver==1.4.5 
matplotlib==3.8.0 monotonic==1.6 
mpmath==1.3.0 numpy==1.26.0 
onnxruntime==1.15.1 overrides==7.4.0 
packaging==23.1 pandas==2.1.0 
Pillow==10.0.1 posthog==3.0.2 
protobuf==4.24.3 pulsar-client==3.3.0
pydantic==1.10.12 
pyparsing==3.1.1 
PyPika==0.48.9 
pyreadline3==3.4.1 
python-dateutil==2.8.2 
python-dotenv==1.0.0 
pytz==2023.3.post1 
PyYAML==6.0.1 
requests==2.31.0 
scikit-learn==1.3.0 
scipy==1.11.2 
six==1.16.0 
sniffio==1.3.0 
starlette==0.27.0 
sympy==1.12 
threadpoolctl==3.2.0 
tokenizers==0.14.0 
tqdm==4.66.1 
typing_extensions==4.7.1 
tzdata==2023.3 
urllib3==2.0.4 
uvicorn==0.23.2 
watchfiles==0.20.0 
websockets==11.0.3

如果你想跟着做,可以建一个类似的文件夹结构,包含数据管道和数据的文件夹(或者修改管道里.csv文件的路径)。

1. 文本到嵌入

第一个管道"01_text_to_embeddings.py"用嵌入模型转换那 10 个示例句子,结果保存在"embeddings_df"里,然后导出成 CSV 文件"embeddings_df.csv"。记得把 HuggingFace 令牌换成你自己的。

############################################################################################################# 
'''
以下脚本将字符串列表"text_chunks"转换为向量嵌入,并将
包含"text_chunks"和"embeddings"的 DataFrame 保存在 csv 文件"embeddings_df.csv"中
''' 
########################################################################################################## 
import os 
import requests 
import pandas as pd 
import numpy as np 

# hugging face token
os.environ['hf_token'] = os.environ.get('HF_TOKEN') 
# os.environ['hf_token'] = 'testtoken123' 

# 我们要转换成向量嵌入的示例文本片段
text_chunks = [ 
    "天空是蓝色的。",
    "草是绿色的。", 
    "太阳在闪耀。",
    "我喜欢巧克力。",
    "披萨很好吃。",
    "编码很有趣。",
    "玫瑰是红色的。",
    "紫罗兰是蓝色的。",
    "水是生命的必需品。",
    "月球绕地球运行。", 
] 

def _get_embeddings(text_chunk): 
    '''
    使用来自 hugging face 的嵌入模型来计算提供的文本片段的嵌入
    参数:
    - text_chunk(字符串):您想要翻译成嵌入的句子或文本片段
    返回:
    - embedding(list):包含所有嵌入维度的列表
    ''' 
    # 定义您要使用的嵌入模型
    model_id = "sentence-transformers/all-MiniLM-L6-v2" 
    # 您可以在设置页面中找到 hugging face api 的令牌 https://huggingface.co/settings/tokens
    hf_token = os.environ.get('hf_token') 
    # 嵌入模型的 API 端点
    api_url = f"https://api-inference.huggingface.co/pipeline/feature-extraction/{model_id}"
    headers = {"Authorization": f"Bearer {hf_token}"} 
    #调用API
    response = requests.post(api_url, headers=headers, json={"inputs":text_chunk,"options":{"wait_for_model":True}}) 
    #将嵌入模型的响应加载为 json 格式
    embedding = response.json() 
    return embedding 

def from_text_to_embeddings(text_chunks): 
    '''
    将句子翻译成向量嵌入
    属性:
    - text_chunks (list):示例字符串列表
    返回:
    - embeddings_df (DataFrame):具有"text_chunk"和"embeddings"列的数据框
    ''' 
    # 使用文本块列表创建新的数据框
    embeddings_df = pd.DataFrame(text_chunks).rename(columns={0:"text_chunk"}) 
    # 使用 _get_embeddings 函数检索每个句子的嵌入
    embeddings_df["embeddings"] = embeddings_df["text_chunk"].apply(_get_embeddings) 
    # 将嵌入列拆分为每个向量维度的单独列
    embeddings_df = embeddings_df['embeddings'].apply(pd.Series) 
    embeddings_df["text_chunk"] = text_chunks 
    return embeddings_df 

# 获取每个文本块的嵌入
embeddings_df = from_text_to_embeddings(text_chunks) 
# 将包含文本块和嵌入的数据框保存为 csv
embeddings_df.to_csv('../02_Data/embeddings_df.csv', index=False)

现在你应该在文件夹"02_Data"里找到 csv 文件"embeddings_df.csv",它看起来像这样:

2. 使用主成分分析在二维中绘制 384 维向量

有了 csv 文件,我们试试把它可视化。用主成分分析提取两个最重要的"主成分",画出来。当然,两个维度装不下所有原始信息,但至少能让你感受到把文本转成向量后发生了什么。

新建一个文件,我叫它"02_create_PCA_analysis.py":

  1. 从"embeddings_df.csv"加载嵌入
  2. 用 scikit-learn 做主成分分析,找到两个最重要的成分
  3. 创建散点图,展示结果
####################################################################################################### 
# 使用嵌入 df 创建 PCA 图
################################################################################################# 
import pandas as pd 
from sklearn.decomposition import PCA 
import matplotlib.pyplot as plt 

def create_pca_plot(embeddings_df): 
    '''
    该函数执行主成分分析以将维度减少到 2,以便我们可以将它们打印在图中
    参数
    - embeddings_df (DataFrame):包含"text_chunk"和"embeddings"列的数据框
    返回
    - df_reduced (DataFrame):包含 2 个最相关主成分的数据框
    ''' 
    # 使用 2 个成分执行 PCA
    pca = PCA(n_components=2) 
    # 将主成分分析应用于嵌入表
    df_reduced = pca.fit_transform(embeddings_df[embeddings_df.columns[:-2]]) 
    # 创建一个具有降低维度的新 DataFrame
    df_reduced = pd.DataFrame(df_reduced, columns=['PC1','PC2']) 
    ############################################################################################## 
    # 创建散点图
    ############################################################################################## 
    def create_scatter_plot(df_reduced): 
        plt.scatter(df_reduced['PC1'], df_reduced['PC2'], label=df_reduced['PC2']) 
        # 添加标签和标题
        plt.xlabel('X') 
        plt.ylabel('Y') 
        plt.title('散点图') 
        # 为每个点添加标签
        for i, label in enumerate(embeddings_df.iloc[:,-1].to_list()): 
            plt.text(df_reduced['PC1'][i], df_reduced['PC2'][i], label) 
        # 保存并显示图
        plt.sa vefig('../02_Data/principal_component_plot.png',format = 'png') 
    # 创建并保存散点图
    create_scatter_plot(df_reduced=df_reduced) 
    return df_reduced

# 将 embeddings_df.csv 加载到数据框中
embeddings_df = pd.read_csv('../02_Data/embeddings_df.csv') 
# 使用函数 create_pca_plot 来
df_reduced = create_pca_plot(embeddings_df)

这个例子可能不算完美,但至少能看到:关于食物和饮料的句子倾向于图的右侧,描述天气的两个句子在左侧靠得很近,关于花的两句在图的底部。我权当这是模型在映射语义上取得了一些成功的(不太严谨的)证据。

人类在二维空间里很容易看出哪些点更接近。但在几百、几千维的向量空间里,怎么量化这种接近程度?——得有一个度量,用来描述相似性。我们计算空间中点之间的距离。

3. 计算距离

距离是怎么定义的?

统计学里有好几个指标用来衡量点之间的距离,常用的一个是"余弦相似度",下面我们就用这个。

我们希望用向量数据库尽快找到相似的条目。但为什么不能直接通过计算每个数据点的距离度量来做相似性搜索呢?

为了理解为什么需要替代方法,我做了一个简单的流程来找最近的数据点:计算每个点的距离,然后排序找到最近邻。

下面这个例子用到前面已经转成向量的 10 条文本。

常见的查询可能是这样的:假设我们有一个新句子(用户查询),比如"百合是白色的"。

  • 首先,获取新句子的嵌入。这个向量给出了它在嵌入空间里的位置,可以用来和集合里的其他文本块比较。

  • 然后计算到每个数据点的距离(这里是余弦相似度)

  • 通常我们关心 k-最近邻,只需过滤出距离最小的那些

我们用句子"百合是白色的"来做例子,计算它与数据集中其他句子的余弦相似度。

为此创建第三个管道,我称之为"03_calculate_cosine_similarity.py":

#################################################################################################### 
# 计算查询向量与所有其他嵌入向量之间的余弦相似度
################################################################################################# 
import numpy as np 
from numpy.linalg import norm 
import time 
import pandas as pd 
import os 
import requests 

def _get_embeddings(text_chunk): 
    '''
    使用来自 hugging face 的嵌入模型为提供的文本片段计算嵌入
    参数:
    - text_chunk(字符串):您想要翻译成嵌入的句子或文本片段
    返回:
    - embedding(list):包含所有嵌入维度的列表
    ''' 
    # 定义您要使用的嵌入模型
    model_id = "sentence-transformers/all-MiniLM-L6-v2" 
    # 你可以在设置页面中找到 hugging face api 的令牌 https://huggingface.co/settings/tokens
    hf_token = os.environ.get('HF_TOKEN') 
    # 嵌入模型的 API 端点
    api_url = f"https://api-inference.huggingface.co/pipeline/feature-extraction/{model_id}"
    headers = {"Authorization": f"Bearer {hf_token}"} 
    # 调用 API
    response = requests.post(api_url, headers=headers, json={"inputs": text_chunk, "options":{"wait_for_model": True}}) 
    # 将嵌入模型的响应加载为 json 格式
    embedding = response.json() 
    return embedding 

def calculate_cosine_similarity(text_chunk, embeddings_df): 
    '''
    计算查询句子和其他每个句子之间的余弦相似度
    1. 获取文本块的嵌入
    2. 计算文本块的嵌入与数据框中每个其他条目之间的余弦相似度
    参数:
    - text_chunk(字符串):我们要用来在数据库中查找类似条目的文本片段(embeddings_df)
    - embeddings_df(DataFrame):包含"text_chunk"和"embeddings"列的数据框
    返回:
    - 
    '''
    # 使用 _get_embeddings 函数检索文本块的嵌入
    sentence_embedding = _get_embeddings(text_chunk) 
    # 将向量嵌入的所有维度组合成一个数组
    embeddings_df['embeddings_array'] = embeddings_df.apply(lambda row: row.values[:-1], axis=1) 
    # 启动计时器
    start_time = time.time() 
    print(start_time) 
    # 创建一个列表来存储计算出的余弦相似度
    cos_sim = [] 
    for index, row in embeddings_df.iterrows(): 
        A = row.embeddings_array 
        B = sentence_embedding 
        # 计算余弦相似度
        cosine = np.dot(A,B)/(norm(A)*norm(B)) 
        cos_sim.append(cosine) 
    embeddings_cosine_df = embeddings_df 
    embeddings_cosine_df["cos_sim"] = cos_sim 
    embeddings_cosine_df.sort_values(by=["cos_sim"], ascending=False) 
    # 停止计时器
    end_time = time.time() 
    # 计算计算相似度所需的时间
    elapsed_time = (end_time - start_time) 
    print("执行时间:", elapsed_time, "seconds") 
    return embeddings_cosine_df 

# 将 embeddings_df.csv 加载到数据框中
embeddings_df = pd.read_csv('../02_Data/embeddings_df.csv') 
# 测试查询句子
text_chunk = "Lilies are white." 
# 计算余弦相似度
embeddings_cosine_df = calculate_cosine_similarity(text_chunk, embeddings_df) 
# 将包含文本块和嵌入的数据框保存到 csv
embeddings_cosine_df.to_csv('../02_Data/embeddings_cosine_df.csv', index=False) 
# 根据相似度进行排名
similarity_ranked_df = embeddings_cosine_df[["text_chunk","cos_sim"]].sort_values(by=["cos_sim"], ascending=False)

看相似度分数,"紫罗兰是蓝色的"和"玫瑰是红色的"与查询"百合是白色的"更接近,而"编程很有趣"就差远了。这个结果算是合理。

相似性搜索似乎有效,那为什么还需要别的办法?

我用 time.time() 记录了计算这 10 个向量余弦相似度的时间,大约 0.005 秒。

把每个点和其他所有点比较,叫作"穷尽搜索",时间是线性的。比如比较 10 个点用了 0.005 秒,那么比较 100 万个文本块就要几分钟。

所以我们需要更高效的相似性搜索方法。

如何加速相似性搜索?

近似最近邻算法(ANN)就是干这个的——它找的邻居可能不是绝对最近的,但速度飞快。这种用准确度换速度的取舍,在 LLM 应用中通常可以接受,因为速度往往更重要,而且相同的信息可能出现在多个文本片段里。(Trabelsi, 2021)

其实普通用户不需要深入了解索引技术,但完全不知道也不好,所以这里用一个倒排文件索引的例子让你有个基本概念,也明白准确度会在哪里损失。

倒排文件索引(IVF)是常用方法,它先把整个集合划分成多个分区,每个分区有一个质心(centroid),每个项目只属于一个分区。搜索时,我们只需要在距离最近的质心所在的分区里查找,就能快速定位。

如果我们要找附近的点,通常只在离查询点最近的质心内搜索就够了。但万一目标点恰好位于相邻分区的边缘,就可能被漏掉。

为了避免这个问题,可以多搜几个分区。但潜在问题依然存在——我们会损失一部分准确度。不过这通常没关系,速度才是王道。

Chroma 支持多种 ANN 算法,包括 HNSW、IVFADC 和 IVFPQ。(Yada v, 2023)

  • 分层可导航小世界(HNSW):创建层次化的图结构,以最少的内存快速存储和搜索高维向量。
  • 带乘积量化的倒排文件(IVFPQ):在索引前用乘积量化压缩向量,实现可处理海量数据集的高精度搜索。

想深入了解不同索引方法的工作原理?强烈推荐 James Briggs 的 YouTube 频道,还有 Peggy Chang 关于 IVF、PQ 的博客文章。

那对我们来说真正重要的是什么?

关键点:通过索引步骤,我们把嵌入存成一种便于快速查找"相似"向量的形式,而不用每次计算所有数据点的距离。代价是用一些准确度换了速度。

对于大多数任务,这个准确度已经足够好了。毕竟把语言转成嵌入本身就不是一门精确科学——同一个词在不同语境或地区可能有不同含义。所以损失几个准确度点通常无所谓,响应速度更重要。

Google 的快速响应正是它成功的原因。对我们来说,每个步骤的速度都至关重要,因为不仅要做向量搜索,还要把问题和上下文喂给 LLM。

这个流程比 Google 搜索要长一点,我想这也是 Bing Chat(用了 LLM)还没能征服世界的原因——只差几毫秒或几秒,但这点差距足以让 Google 保持领先。

为了说明这些步骤,假设我们要做一个像 Bing Chat 那样的聊天机器人:

依然有(传统的)搜索部分来找到最相关的内容和新闻,然后才把结果喂给 LLM,让 LLM 解释数据并生成听起来不错的回答。

向量存储负责在加载数据时做 tokenize、嵌入和索引。数据一旦入库,就可以用新数据点查询它了。

假设我们决定使用向量存储——我们有什么选择?

向量数据库有不同的形态:

  • 纯向量数据库
  • SQL、NoSQL 或文本搜索数据库中的扩展功能
  • 简单的向量库

文本搜索数据库可以在大量文本中搜特定词或短语。最近,一些文本搜索数据库也开始用向量搜索来提升搜索能力。在上一届微软开发者大会上,Elasticsearch 就展示了如何结合传统搜索和向量搜索,创建"混合评分"系统来提供最佳结果。

向量搜索也在被越来越多的 SQL 和 NoSQL 数据库采纳,比如 Redis、MongoDB、Postgres。Pgvector 是 Postgres 的开源向量相似性搜索扩展,支持(Github, 2023):

  • 精确和近似最近邻搜索
  • L2 距离、内积和余弦距离

对于较小项目,向量库是不错的选择,涵盖了大部分所需功能。

2017 年,Facebook AI Research 发布了 FAISS,这是最早的一批向量库之一。FAISS 是一个用于高效搜索和聚类密集向量的库,能处理任何大小的向量集,甚至内存放不下的也没问题。它用 C++ 编写,附带了 Python 包装器,方便数据科学家集成。(FAISS 文档, 2023)

另一方面,Chroma、Pinecone、Wea viate 是纯向量数据库,能像普通数据库一样存储和搜索向量数据。本文我会教你怎么用 Chroma 搭建向量数据库并填充数据。如果你想要快速解决方案,像 FAISS 这样的向量库可以帮助你轻松起步。

那到底该选哪个?

最终还是要根据你自己的项目需求来定,但别把简单的事情复杂化。Andrej Karpathy 在 Twitter 上说过:

"np.array——如今人们追求更花哨的东西的速度太快了"——Andrej Karpathy


如果你只是需要搜索几页 PDF 或文本文件,完全可以用 np.array 或 pandas 数据框来存嵌入。只有当你要处理成百上千甚至上百万个向量、并且需要定期搜索时,向量数据库才真正体现出价值。本文我用 Chroma,但原理对所有数据库都通用。

动手教程——设置你的第一个矢量商店

为了把内容存成向量并提升相似性搜索的性能,我们需要搭一个自己的向量数据库。Chroma 是个很好的开源选择,免费且拥有 Apache 2.0 许可证。其他替代方案包括 FAISS、Wea viate、Pinecone,有些开源免费,有些只提供商业服务。

几步就能让 chromadb 跑起来。只需要用 pip 下载 chromadb 库,然后就可以开始设置第一个向量存储数据库了。

1. 安装 chroma

https://pypi.org/project/chromadb/

pip install chromadb

2. 获取/创建 chroma 客户端和集合

集合是专门存放嵌入、文档和其他元数据的存储区域。如果你想保存以便之后使用索引和集合,可以用"persist_directory"参数。

import chromadb
from chromadb.config import Settings
import pandas as pd 

# 向量存储设置
VECTOR_STORE_PATH = r'../02_Data/00_Vector_Store'
COLLECTION_NAME = 'my_collection' 

# 将 embeddings_df.csv 加载到数据框中
embeddings_df = pd.read_csv('../02_Data/embeddings_df.csv') 

def get_or_create_client_and_collection(VECTOR_STORE_PATH, COLLECTION_NAME): 
    # 获取/创建 chroma 客户端
    chroma_client = chromadb.PersistentClient(path=VECTOR_STORE_PATH) 
    # 获取或创建集合
    collection = chroma_client.get_or_create_collection(name=COLLECTION_NAME) 
    return collection 

# 获取或创建集合
collection = get_or_create_client_and_collection(VECTOR_STORE_PATH, COLLECTION_NAME)

3. 添加一些文本文档到集合中

Chroma 让你轻松存储和组织文本文档。它会自动处理 tokenize、嵌入和索引。如果你已经自己创建了嵌入,也可以直接加载到 Chroma 的向量存储里。

我们把之前的数据框"embeddings_df"存入新的数据存储:

# 将 embeddings_df.csv 加载到数据框中
embeddings_df = pd.read_csv('../02_Data/embeddings_df.csv') 

def add_to_collection(embeddings_df): 
    # 将样本条目添加到集合
    # collection.add( 
    #     documents=["This is a document", "This is another document"], 
    #     metadata=[{"source": "my_source"}, {"source": "my_source"}], 
    #     ids=["id1", "id2"] 
    # ) 
    # 将向量嵌入的所有维度组合成一个数组
    embeddings_df['embeddings_array'] = embeddings_df.apply(lambda row: row.values[:-1], axis=1) 
    embeddings_df['embeddings_array'] = embeddings_df['embeddings_array'].apply(lambda x: x.tolist()) 
    # 将数据框添加到集合
    collection.add( 
        embeddings=embeddings_df.embeddings_array.to_list(), 
        documents=embeddings_df.text_chunk.to_list(), 
        # 创建一个字符串列表作为索引
        ids=list(map(str, embeddings_df.index.tolist())) 
    ) 

# 将 embeddings_df 添加到我们的向量存储集合
add_to_collection(embeddings_df)

4. 从数据库提取所有条目到 excel 文件

导出向量存储中所有条目:

def get_all_entries(collection): 
    # 查询集合
    existing_docs = pd.DataFrame(collection.get()).rename(columns={0:"ids",1:"embeddings",2:"documents",3:"metadatas"}) 
    existing_docs.to_excel(r"..//02_Data//01_vector_stores_export.xlsx") 
    return existing_docs 

# 提取向量存储集合中的所有条目
existing_docs = get_all_entries(collection)

5. 查询集合

Chroma 能轻松找到与查询文本最相似的 n 个结果:

def query_vector_database(VECTOR_STORE_PATH, COLLECTION_NAME, query, n=2): 
    # 查询集合
    results = collection.query( 
        query_texts=query, 
        n_results=n 
    ) 
    print(f"相似性搜索:{n}最相似的条目:") 
    print(results["documents"])
    return results

# 相似性搜索
similar_vector_entries = query_vector_database(VECTOR_STORE_PATH, COLLECTION_NAME, query=["Lilies are white."])

这个例子会返回向量存储中最相似的两个条目。

下面是一个完整的汇总脚本:

################################################################################################################ 
'''
包括一些用于创建新的向量存储集合、填充和查询它的函数
''' 
####################################################################################################### 
import chromadb 
from chromadb.config import Settings 
import pandas as pd 

# 向量存储设置
VECTOR_STORE_PATH = r'../02_Data/00_Vector_Store'
COLLECTION_NAME = 'my_collection' 

# 将 embeddings_df.csv 加载到数据框中
embeddings_df = pd.read_csv('../02_Data/embeddings_df.csv') 

def get_or_create_client_and_collection(VECTOR_STORE_PATH, COLLECTION_NAME): 
    # 获取/创建 chroma 客户端
    chroma_client = chromadb.PersistentClient(path=VECTOR_STORE_PATH) 
    # 获取或创建集合
    collection = chroma_client.get_or_create_collection(name=COLLECTION_NAME) 
    return collection 

# 获取或创建集合
collection = get_or_create_client_and_collection(VECTOR_STORE_PATH, COLLECTION_NAME) 

def add_to_collection(embeddings_df): 
    # 将样本条目添加到集合
    # collection.add( 
    #     documents=["This is a document", "This is another document"], 
    #     metadatas=[{"source": "my_source"}, {"source": "my_source"}], 
    #     ids=["id1", "id2"] 
    # ) 
    # 将向量嵌入的所有维度组合成一个数组
    embeddings_df['embeddings_array'] = embeddings_df.apply(lambda row: row.values[:-1], axis=1) 
    embeddings_df['embeddings_array'] = embeddings_df['embeddings_array'].apply(lambda x: x.tolist()) 
    # 将数据框添加到集合
    collection.add( 
        embeddings=embeddings_df.embeddings_array.to_list(), 
        documents=embeddings_df.text_chunk.to_list(), 
        # 创建一个字符串列表作为索引
        ids=list(map(str, embeddings_df.index.tolist())) 
    ) 

# 将 embeddings_df 添加到我们的向量存储集合
add_to_collection(embeddings_df) 

def get_all_entries(collection): 
    # 查询集合
    existing_docs = pd.DataFrame(collection.get()).rename(columns={0:"ids",1:"embeddings",2:"documents",3:"metadatas"}) 
    existing_docs.to_excel(r"..//02_Data//01_vector_stores_export.xlsx") 
    return existing_docs 

# 提取向量存储集合中的所有条目
existing_docs = get_all_entries(collection) 

def query_vector_database(VECTOR_STORE_PATH, COLLECTION_NAME, query, n=2): 
    # 查询集合
    results = collection.query( 
        query_texts=query, 
        n_results=n 
    ) 
    print(f"相似性搜索:{n}最相似的条目:") 
    print(results["documents"]) 
    return results 

# 相似性搜索
similar_vector_entries = query_vector_database(VECTOR_STORE_PATH, COLLECTION_NAME, query=["Lilies are white."])

然后我们就可以用这些 k-最近邻来作为 LLM 的输入。简单搭个提示模板,把找到的文本块插进去,就可以发给 GPT、LLaMA 或任何你喜欢的 LLM 了。我之前的文章里描述过这个流程。

摘要

随着机器学习模型现在能准确地把各种内容转成向量,向量搜索越来越受欢迎。不光是专用向量数据库越来越多,现有的 SQL、NoSQL 和文本搜索数据库也在把向量搜索功能纳入产品,要么改进搜索机制,要么为有向量搜索需求的用户提供产品。

对向量存储的兴趣与日俱增。得益于 Transformer 模型的进步,我们现在可以放心地把文本块转成向量,这为文本处理打开了无限的数学可能性。

关于宇宙的好的网名有哪些
关于宇宙的好的网名有哪些

类型:角色扮演

大小:1

语言:简体中文

平台:互联网

游戏下载

热门手游

手机号码测吉凶
本站所有软件,都由网友上传,如有侵犯你的版权,请发邮件haolingcc@hotmail.com 联系删除。 版权所有 Copyright@2012-2013 haoling.cc