文章略长,分成两部分。
PostgreSQL已经成为顶级的关系型数据库有一段时间了。现在,在人工智能时代,我们还需要存储向量数据以实现快速且相似的搜索。这就是pgvector的用武之地------它就像PostgreSQL的一个插件,帮助它很好地处理向量数据。
什么是向量数据?
想象你有一张地图,但上面不是城市和街道,而是你最喜欢的东西:电影、音乐和有趣的猫视频。每样东西都像地图上的一个点,如果这些点很接近,就意味着它们很相似。例如,一部喜剧电影的点可能靠近另一部喜剧,但与科幻电影的点就不那么接近。在数字世界中,向量就像地图上的点,代表信息,如文本或图像。通过比较这些点的接近程度,我们可以判断事物的相似性,即使它们使用不同的词汇或看起来有点不同。这就像一种计算机能理解的超越文字或像素含义的秘密语言------有点像魔法,但用的是数学!我们将向量数据表示为浮点数列表。
例如:[0.5, 1.2, -0.3, 4.7] 代表一个四维向量。
pgvector - 文本搜索的革命
想象一下普通的文本搜索就像一个侦探仔细地逐字检查文件,以找到匹配的内容。现在,pgvector更像是一个AI代理。它将文本、图像或声音存储为高维向量,就像捕捉数据核心含义的数字指纹。如上所述,如果两个向量在这个空间中很接近,就意味着它们在意义上非常相似。这就是余弦距离的用武之地。这种余弦度量查看两个向量之间的角度,如果角度小,就意味着它们非常相似。
本地安装pgvector
为了便于使用,我们将pgvector打包成了一个docker镜像,里面还包含了其他一些常用的功能,比如时序数据库timescaledb、空间数据库插件postgis等。
下载方法:
bash
docker pull nimblex/memfiredb
使用方法:
css
docker run --name memfiredb -p 5432:5432 -e POSTGRES_PASSWORD=memfiredb -d nimblex/memfiredb
创建pgvector:
arduino
create extension vector;
我们也提供了在线直接使用的方式,可以在文末找到链接。
pgvector解决了什么问题?
pgvector旨在解决PostgreSQL在搜索和数据表示方面的几个关键问题:
- 有限的文本和数据表示:传统的PostgreSQL以简单的格式存储数据,如文本、数字和日期。这种缺乏灵活表示的方式使得捕捉复杂数据点(如文本文档、图像或科学数据)之间的真实含义和关系变得困难。pgvector引入了多维向量作为一种数据类型,允许基于其固有质量和关系对数据进行更丰富的表示。此外,pgvector的基于向量的方法,结合HNSW和IVFFlat等索引技术,使得在大型数据集上进行更快速、更高效的搜索成为可能,为实时应用程序和大数据环境提供可扩展性。
- 低效的相似性搜索:传统的PostgreSQL文本搜索依赖于关键字匹配或模式识别,这在寻找相似文档或内容时可能会很慢,也不能用于执行基于数据含义的搜索。pgvector基于余弦距离等高级距离度量实现高效的相似性搜索,允许您找到即使不共享相同单词或模式也具有相似含义的项目。
- 缺乏与机器学习的集成:pgvector通过使PostgreSQL与机器学习模型生成的向量嵌入无缝集成,弥合了PostgreSQL和机器学习之间的差距。这使您能够直接在PostgreSQL数据库中利用机器学习的强大功能,用于文档检索、图像搜索和推荐系统等任务。
pgvector支持两种主要类型的搜索------精确近邻搜索(ENN)和近似近邻搜索(ANN)。ENN和ANN都专注于在高维空间中寻找相似的向量,但它们的方法不同,也有不同的优缺点
精确近邻搜索(ENN)定义:
- 在数据集中找到与查询向量完全相同的向量。
- 算法:采用暴力方法,将查询向量与数据库中的每个向量进行比较。
- 准确性:保证找到真正的最近邻,确保最高可能的精度。
- 性能:需要将查询向量与所有可用向量进行比较,导致可能的慢速计算和高资源消耗,特别是对于大型数据集。
- 常见用例:当绝对精度至关重要时,例如在关键的财务计算或科学分析中。
近似近邻搜索(ANN):
- 在一定容差内找到与查询向量大致相似的向量。
- 利用索引技术识别候选向量,而无需将它们全部比较,显著减少搜索空间。
- 提供近似答案,可能错过真正的最近邻,但通常返回接近的替代品。
- 与ENN相比,提供更快的搜索时间和更低的资源消耗,适合大型数据集和实时应用程序。
- 当实时响应和可扩展性优先于绝对精度时,例如在推荐系统、图像/视频检索和自然语言处理应用程序中。
选择ENN还是ANN取决于您的具体需求:
- 准确性:如果您需要以最高精度找到绝对最接近的匹配,ENN是您的选择。
- 性能:对于实时应用程序和大型数据集,ANN提供了更快的速度和资源效率。
- 数据大小:数据集越大,人工神经网络的性能优势就越显著。
- 对不准确的容忍度:想想在你的应用程序中有一个势均力敌的匹配是多么好。
额外因素:
- 距离度量:ENN和ANN都可以使用各种距离度量,如L2、内积和余弦距离。
- 索引技术:不同的ANN算法,如HNSW和IVF,提供不同的性能和准确性权衡。
L2距离、内积和余弦距离的简单示例解释:
想象两个苹果在一个水果篮子里:
- L2距离:这告诉你篮子里苹果之间的距离,就像测量它们之间的直线距离。距离越大,它们越不相似。当你需要知道事物之间的确切差异时,比如地图上的距离或价格差异,就会使用它。
- 内积:这就像比较苹果的味道和质地。高内积意味着它们都是甜的和脆的,而低内积意味着它们不同(也许一个是酸的,另一个是软的)。当你想要比较事物在特定品质上的相似性时,比如比较具有相似主题的文档或具有相似节奏的音乐时,就会使用它。
- 余弦距离:假设每个苹果顶部都有一个小箭头,指向某个方向。现在,如果你想要知道这些苹果有多相似,你可以检查它们箭头之间的角度。当你想要知道事物在一般意义上的对齐程度时,比如找到相似的图片或根据你过去的购买推荐相关产品时,就会使用它。
注意:pgvector本身并不直接提供基于内积或余弦距离的内置搜索。然而,它以不同的方式利用L2距离来实现类似的结果。
pgvector中的索引:
pgvector提供两种类型的索引,都是近似最近邻(ANN)索引,用于加速搜索相似向量。选择正确的索引取决于几个因素。以下是它们的关键差异:
- 特征:将向量空间划分为分区,构建类似图结构的相似向量。
- 构建时间:显著更快,显著更慢。
- 内存使用:较少内存,更多内存。
- 准确性:通常对高维向量不太准确,尤其是对于高维向量,通常具有更好的准确性。
- 适应性:可能不适用于所有向量分布,很好地处理各种向量分布。
理解IVFFlat和HNSW的真实世界示例:
想象你住在一个城市,需要找到一个与你喜欢的餐厅相似的餐厅(一个"最近邻")。
- IVFFlat就像有一张带有标签的社区地图。
- 根据一般特征将城市划分为不同的区域。
- 可以快速告诉你哪些社区可能有类似的餐厅。
- 引导你到那些社区内的餐厅,可能接近,但不一定是每个方面都最相似的。
- 快速开始使用地图(更快的索引构建)。
- 需要较少的内存来存储地图。
- HNSW就像有一个了解城市的当地向导。
- 了解城市如何连接餐厅(相似的口味、氛围等)。
- 可以快速引导你到在整体体验上接近你最喜欢的餐厅的餐厅,即使它们不完全相同。
- 需要向导花更多时间了解城市(构建索引)。
- 需要向导记住更多信息(更高的内存使用)。
选择正确的方法取决于你的优先级:HNSW就像向导,优先找到最相似的餐厅,即使需要更长的时间。速度:IVFFlat就像地图,专注于快速找到类似的餐厅,即使它不是完美的。HNSW适用于复杂的城市布局(高维数据),而IVFFlat适用于大城市(大型数据集)。
数据嵌入过程
要创建向量数据,我们首先需要生成它。创建向量数据的过程称为嵌入,它就像一种秘密语言,弥合了计算机数字形式的单词、图像或声音世界之间的差距。
-
收集数据:想象一个满是书籍的书架,一个充满艺术作品的艺术画廊,或者一起播放的一堆旋律。当我们开始嵌入时,意味着我们正在收集我们想要以向量形式展示的基本元素。
-
选择嵌入模型:有不同类型的嵌入模型可用于特定类型的信息。一些常见的模型包括:
-
- HuggingFace或OpenAI的text-embedding-ada-002用于文本
- VGG16或ResNet50用于图像
- Audio2Vec用于声音
-
训练模型:这就是魔法发生的地方!模型分析数据,识别模式、关系和隐藏的含义。它将单词、图像或声音映射到它们对应的向量,为每一个创建独特的数值表示。
-
生成向量:一旦模型学会了数据的语言,它就可以根据需求将新输入转换为向量。
HuggingFace或OpenAI嵌入模型 - 如何选择?
选择Hugging Face和OpenAI来满足您的嵌入需求取决于几个因素:
- Hugging Face:侧重于广度和灵活性,提供广泛的自然语言处理模型,用于各种任务。大多数模型都是免费的,某些特定模型或API可能需要付费。易于使用,有工具和社区支持。开源工具提供更多控制,但对模型定制的控制较少。
- OpenAI:专注于高性能嵌入模型,提供专门化的嵌入模型。通过计划访问,需要付费。API或Hugging Face模型中心访问,对模型定制的控制较少。
最终,最佳选择取决于您的具体需求、预算和技术专长。在做出决定之前,仔细考虑您的优先事项,并比较可用的选项。
利用pgvector:实际示例
想象一个场景,我们在数据库中以向量形式存储各种单词。然后我们提供不同的单词给数据库,以找到最相似的单词。
我们将存储在数据库中的单词是Apple、Banana、Cat和Dog。我们要搜索的单词是Mango和Horse。
创建扩展:
一旦您在计算机上安装了pgvector,首先需要创建所需的扩展。
arduino
CREATE EXTENSION vector;
创建表:
为了存储向量数据:
sql
CREATE TABLE items (id bigserial PRIMARY KEY, name varchar(100), embedding vector);
生成嵌入:
我们将使用HuggingFace来生成我们的嵌入。
安装所需的Python包:
pip3 install sentence-transformers
pip3 install langchain
Python代码以从langchain生成嵌入:
python
from langchain.embeddings import HuggingFaceEmbeddings
embeddings = HuggingFaceEmbeddings(model_name='all-MiniLM-L6-v2')
print(embeddings.embed_query("Apple"))
这里使用的模型是all-MiniLM-L6-v2。
下面的图表显示了单词Apple的嵌入。
将数据插入数据库:
sql
INSERT INTO items (name, embedding) VALUES ('Apple', '<Your_Apple_Embedding>');
将Your_Apple_Embedding替换为上面图表中显示的原始嵌入。
类似地,我们将插入其他向量,即Banana、Cat和Dog。
插入所有关键词后,我们可以检查items表中的行数。
sql
SELECT COUNT(*) FROM items;
向量搜索:
为了找到一个单词,我们将再次为该单词创建嵌入,并利用生成的嵌入在数据库中搜索该单词。
将生成的Mango嵌入插入到下面的查询中以获取结果:
sql
SELECT name, 1 - (embedding <-> '<Mango_embedding>') AS cosine_similarity FROM items ORDER BY cosine_similarity DESC LIMIT 2;
SELECT查询结构解析:
SELECT name
:这从items表中选择项目的名称。1 - (embedding <-> '<Horse_embedding>') AS cosine_similarity
:这个表达式计算每个项目的嵌入与指定的"Horse"嵌入之间的余弦相似度,并将结果别名为cosine_similarity列。embedding <-> '<Horse_embedding>'
:这个操作符计算两个向量之间的余弦相似度。它将每个项目的嵌入(存储在embedding列中)与"Horse"嵌入的占位符进行比较。1 -- ...
:相似度计算的结果从1中减去,以反转比例,使更高的值代表更大的相似性。ORDER BY cosine_similarity DESC
:这根据计算出的余弦相似度以降序排序结果。这将最相似于"Horse"的项目放在顶部。LIMIT 2
:这限制输出仅为基于计算出的余弦相似度的前两个最相似的项目。
结果分析:
Mango和horse并不直接出现在表中:如果它们在表中列出,那么将返回完美的匹配(余弦相似度为1)。Banana和Apple对Mango有适度的语义相似性。Banana(0.5249995745718354)是最相似的,但不是完全匹配。Apple(0.4006202280151715)的相似度较低,表明关系较弱。Dog和Cat对Horse有适度的语义相似性。Dog(0.5349170288394121)是最相似的,但不是完全匹配。Cat(0.41092748143620783)的相似度较低,表明关系较弱。值得注意的是,余弦相似度的范围是从0到1,1表示完全匹配,意味着项目在用于比较的表示方面是相同的。0表示完全不相似,意味着项目在它们的表示中没有任何重叠。介于0和1之间的值代表不同程度的相似性。
在线使用pgvector:cloud.memfiredb.com/auth/login?...
原文:AI Meets PostgreSQL - The pgvector Revolution in Text Search - Stormatics