向量数据库:AI时代的数据基础设施

向量数据库是一种专门用于存储、索引和查询向量数据的数据库系统。在人工智能和机器学习飞速发展的今天,向量数据库正成为处理非结构化数据的核心基础设施。

是什么

向量数据库是一种新型数据库,它存储的数据不是传统的结构化表格,而是高维向量。这些向量通常是通过深度学习模型将非结构化数据(如文本、图像、音频、视频等)转换而来的数值表示。

与传统关系型数据库不同,向量数据库的核心功能不是基于精确匹配的查询,而是基于相似度匹配。它能够快速地从海量向量中找出与查询向量最相似的向量,这使得它在处理复杂的非结构化数据时具有独特的优势。
在这里插入图片描述

原理

向量数据库的核心原理包括以下几个方面:

1. 向量表示

任何类型的数据都可以通过深度学习模型转换为向量。例如:

  • 文本可以通过Word2Vec、BERT等模型转换为向量
  • 图像可以通过ResNet、VGG等模型转换为向量
  • 音频可以通过MFCC、WaveNet等模型转换为向量

这些向量捕获了数据的语义信息,使得相似的数据具有相似的向量表示。

2. 相似度计算

向量数据库使用各种距离度量来计算向量之间的相似度,常用的包括:

  • 欧几里得距离(Euclidean Distance):衡量向量空间中两点间的直线距离
  • 余弦相似度(Cosine Similarity):衡量两个向量之间的夹角余弦值,不受向量长度影响
  • 曼哈顿距离(Manhattan Distance):衡量向量空间中两点间的折线距离
  • 汉明距离(Hamming Distance):衡量两个等长向量对应位置不同元素的数量

其中,余弦相似度在处理高维数据时表现尤为出色,因为它不受向量长度的影响。

相似度计算示例

为了更直观地理解这些相似度度量方法,我们以两个简单向量为例进行计算:

  • 向量 A = [1, 2, 3]
  • 向量 B = [4, 5, 6]
1. 欧氏距离

欧氏距离是向量空间中两点间的直线距离,计算公式为:
d(A,B)=∑i=1n(Ai−Bi)2 d(A,B) = \sqrt{\sum_{i=1}^{n} (A_i - B_i)^2} d(A,B)=i=1n(AiBi)2

对于向量 A 和 B:
d(A,B)=(1−4)2+(2−5)2+(3−6)2=9+9+9=27≈5.196 d(A,B) = \sqrt{(1-4)^2 + (2-5)^2 + (3-6)^2} = \sqrt{9 + 9 + 9} = \sqrt{27} \approx 5.196 d(A,B)=(14)2+(25)2+(36)2 =9+9+9 =27 5.196

2. 点积

点积(内积)是两个向量对应元素相乘之和,计算公式为:
[A⋅B=∑i=1nAi×Bi][ A \cdot B = \sum_{i=1}^{n} A_i \times B_i ][AB=i=1nAi×Bi]

对于向量 A 和 B:
A⋅B=1×4+2×5+3×6=4+10+18=32 A \cdot B = 1\times4 + 2\times5 + 3\times6 = 4 + 10 + 18 = 32 AB=1×4+2×5+3×6=4+10+18=32

点积值越大,通常表示向量在相同方向上的分量越多,但它受向量长度影响较大。

3. 余弦相似度

余弦相似度衡量两个向量之间的夹角余弦值,计算公式为:
cos⁡(θ)=A⋅B∥A∥×∥B∥ \cos(\theta) = \frac{A \cdot B}{\|A\| \times \|B\|} cos(θ)=A×BAB

其中,(|A|) 和 (|B|) 分别是向量 A 和 B 的模长(欧氏距离)。

对于向量 A 和 B:

  • ∥A∥=12+22+32=14≈3.7417\|A\| = \sqrt{1^2 + 2^2 + 3^2} = \sqrt{14} \approx 3.7417A=12+22+32 =14 3.7417
  • ∥B∥=42+52+62=77≈8.775\|B\| = \sqrt{4^2 + 5^2 + 6^2} = \sqrt{77} \approx 8.775B=42+52+62 =77 8.775
  • cos⁡(θ)=323.7417×8.775≈3232.83≈0.9747\cos(\theta) = \frac{32}{3.7417 \times 8.775} \approx \frac{32}{32.83} \approx 0.9747cos(θ)=3.7417×8.7753232.83320.9747

余弦相似度接近1,表示两个向量方向非常相似;接近0,表示方向正交;接近-1,表示方向相反。

向量数据库的核心流程
  • 输入:一个查询向量
  • 输出:最相似的几个向量
最近邻搜索问题

最近邻搜索(Nearest Neighbor,NN)是向量数据库的核心问题,涉及多种算法:

  1. 暴力搜索(Brute Force):最精准但耗时长,适用于小规模数据
  2. 聚类算法:聚类算法生成的簇中心可以作为近似最近邻搜索的参考点,以快速定位相似的对象
  3. 近似最近邻(Approximate Nearest Neighbor,ANN):通过牺牲部分精度来换取显著的速度提升
    1. 局部敏感哈希(Locality Sensitive Hashing,LSH):使用哈希函数使相似向量更容易碰撞到同一桶中
    2. 分层导航小世界 (Hierarchical Navigable Small World Graphs) HNSW:构建一个多层图连接点到邻居,实现快速导航到近似邻居

这些算法在搜索质量和搜索时间之间做出了不同的权衡。

3. 索引结构

为了实现高效的相似度查询,向量数据库采用了各种专门的索引结构,主要包括:

  • FLAT索引:简单暴力的全量扫描,适用于小规模数据
  • IVF索引(倒排文件):将向量空间划分为多个聚类,查询时先找到最近的聚类,再在该聚类内搜索
  • HNSW索引(分层导航小世界):通过构建多层图结构加速查询
  • PQ索引(乘积量化):通过量化向量降低存储空间和计算复杂度

应用场景

以图搜图 视频推荐等等

向量数据库在以下场景中发挥着重要作用:

1. 推荐系统

向量数据库能够高效地计算用户兴趣向量与物品向量之间的相似度,从而实现精准推荐。例如:

  • 电商平台的商品推荐
  • 视频平台的内容推荐
  • 音乐平台的歌曲推荐

2. 自然语言处理

向量数据库在NLP领域有着广泛应用:

  • 语义搜索:根据查询的语义而非关键词匹配文档
  • 问答系统:快速找到与问题相关的上下文信息
  • 文本聚类:将相似主题的文本聚在一起

3. 计算机视觉

  • 图像检索:根据图像内容而非标签查找相似图像
  • 人脸识别:快速识别和比对人脸特征
  • 物体检测:识别图像中的物体并分类

4. 多模态数据处理

向量数据库能够处理不同类型的数据(文本、图像、音频等),实现跨模态的相似度查询。例如,根据图像搜索相关的文本描述,或根据文本描述搜索相关的图像。

5. 大模型增强(RAG)

检索增强生成(RAG)是向量数据库的重要应用场景,它通过检索相关文档来增强大语言模型的生成能力,解决了大模型的知识时效性和幻觉问题。

对比

与传统关系型数据库对比

特性 向量数据库 关系型数据库
数据类型 高维向量 结构化数据
查询方式 相似度匹配 精确匹配
索引结构 专门的向量索引(IVF、HNSW等) B+树、哈希索引等
主要应用 非结构化数据处理、AI应用 事务处理、报表分析

主流向量数据库产品对比

产品 特点 优势 劣势
Faiss 开源、高效、支持多种索引 性能出色、内存占用低 不支持分布式部署
Milvus 开源、分布式、支持多种索引 可扩展性强、支持云原生 资源消耗较大
Pinecone 闭源、托管服务 开箱即用、无需维护 成本较高
Qdrant 开源、轻量级、支持过滤条件 安装简单、查询速度快 生态相对薄弱
Chroma 开源、轻量级、专注于开发体验 简单易用、内存占用低、集成度高 不支持分布式部署、高级功能较少

实践

使用 faiss 存储向量并且进行向量检索

FAISS(Facebook AI Similarity Search)是由Facebook人工智能研究院开发的高效 相似度搜索和聚类 库,专为处理大规模高维向量数据设计。

核心特点

  • 高效检索 :提供多种索引类型(如 Flat 、 IVF 、 HNSW 等),支持精确搜索和近似搜索(ANN),平衡搜索速度与精度。
  • 大规模处理 :可轻松处理数十亿级别的高维向量(如10亿+ 128维向量)。
  • 硬件加速 :支持CPU和GPU加速(通过CUDA),显著提升检索性能。
  • 多语言支持 :提供Python和C++接口,易于集成到不同应用中。
'''
相似性搜索

'''
import os
import time

import faiss
import numpy as np


np.random.seed(42) # 保证多次运行结果一致


# 1. 索引创建
def test01():
    dim = 10
    data = np.random.rand(10000, dim)
    idx_name = f"dim_{dim}_datalen_{len(data)}.faiss"
    # dim_1000_datalen_100000 500ms生成向量时间降到了  150 ms    数据占用 380MB

    t1 = time.time()
    if not os.path.exists(idx_name):
        # index = faiss.IndexFlatL2(dim)  # 使用欧式距离计算相似度
        index = faiss.IndexFlatIP(dim)  # 使用点积计算相似度
        # index = faiss.index_factory(dim, "Flat", faiss.METRIC_L2)
        # index = faiss.index_factory(dim, "Flat", faiss.METRIC_INNER_PRODUCT)
        # 添加向量
        index.add(data)
        faiss.write_index(index, idx_name)
    else:
        index = faiss.read_index(idx_name)
    t2 = time.time()
    print('index time:', t2 - t1)
    # 搜索向量
    topK= 1
    query_vectors = np.random.rand(1, dim)
    distances, idx = index.search(query_vectors, k=topK)
    print(distances) # D 就是 点积 或者是 欧氏距离,I 就是搜索结果对应的ID
    print("=======================")
    print(idx) # D 就是 点积 或者是 欧氏距离,I 就是搜索结果对应的ID
    resV = data[idx][0,0]
    print(resV)
    # 计算两个向量的点积
    print("=======================")

    dis = np.sum(query_vectors * resV, axis=1) # 都是 dim*1 的向量
    print(dis)

    print("=======================")
    # 查询最近似向量的ID
    I = index.assign(query_vectors, k=topK)
    print(I)

    # 重建指定位置向量,并不是所有索引都支持该函数
    # print(index.reconstruct(0))

    # # 删除指定 ID 数据
    # index.remove_ids(np.array([1, 2, 3]))
    # print(index.ntotal)
    #
    # # 删除所有向量数据
    # index.reset()
    # print(index.ntotal)



if __name__ == '__main__':
    test01()

如何加速向量检索

'''

IndexFlat 索引是一种基于线性搜索的索引,它通过逐个计算与每个向量的相似度来进行搜索。在数据量较大的时候,搜索效率会较低。
此时,我们可以使用 IndexIVFFlat 索引来提升搜索效率。
它的原理如下:对于所有的向量进行聚类,相当于把所有的数据进行分类。当进行查询时,在最相似的N 个簇中进行线性搜索。
这就减少了需要进行相似度计算的数据量,从而提升搜索效率。

time: 0.0645294189453125
[[28.383003 29.018719]] [[ 54513 682317]]
time: 0.013531208038330078
[[28.383003 29.018719]] [[ 54513 682317]]

搜索簇 数量减少  精度下降,但是 速度提升
这种方法是一种在查询的精度和效率之间平衡的方法
time: 0.06836390495300293
[[28.383003 29.018719]] [[ 54513 682317]]
time: 0.0030002593994140625
[[28.383003 30.071785]] [[ 54513 597587]]

'''
import faiss
import numpy as np
import time


np.random.seed(42)
data = np.random.rand(1000000, 256)
ids = np.arange(0, 1000000)
query_vector = np.random.rand(1, 256)


def test01():

    index = faiss.IndexFlatL2(256)
    index = faiss.IndexIDMap(index)
    # 添加向量
    index.add_with_ids(data, ids)
    # 搜索向量
    s = time.time()
    D, I = index.search(query_vector, k=2)
    print('time:', time.time() - s)
    print(D, I)


def test02():

    # 第一个参数:量化参数
    # 第二个参数:向量维度
    # 第三个参数:质心数量
    quantizer = faiss.IndexFlatL2(256)
    index = faiss.IndexIVFFlat(quantizer, 256, 100) # 100 个质心
    # 增加搜索质心数量可以提高精确度,但是需要更多的时间
    # 默认为1  index.nprobe = 1 表示在搜索过程中只考虑一个聚类中心。
    # 这意味着搜索会在与查询向量最相似的一个聚类中进行线性搜索。这种方法可以显著提高搜索速度,但可能会牺牲一些精度。
    # 10可以  5 不行

    index.nprobe = 10
    # 聚类计算质心
    index.train(data)
    # 添加向量
    index.add_with_ids(data, ids)
    # 搜索向量
    s = time.time()
    D, I = index.search(query_vector, k=2)  # ANN近似最近邻
    print('time:', time.time() - s)
    print(D, I)


if __name__ == '__main__':
    test01() # 暴力搜索
    test02() # 聚类之后再搜索
Logo

为武汉地区的开发者提供学习、交流和合作的平台。社区聚集了众多技术爱好者和专业人士,涵盖了多个领域,包括人工智能、大数据、云计算、区块链等。社区定期举办技术分享、培训和活动,为开发者提供更多的学习和交流机会。

更多推荐