如何在 SQLite 中实现矢量搜索?
在现代应用中,尤其是在自然语言处理(NLP)和机器学习(ML)领域,矢量搜索变得越来越重要。
SQLite 是一种轻量级数据库,虽然没有内置的矢量搜索功能,但通过扩展和一些额外的步骤,可以轻松实现矢量搜索。
什么是sqlite-vec?
sqlite-vec是一个SQLite数据库的向量搜索扩展,添加了向量搜索功能。
该扩展完全用C语言编写,无依赖性,具有极好的可移植性,可以在大多数操作系统和环境中运行,且具有MIT/Apache-2.0双重许可证。
sqlite-vec 是一个极小且“足够快”的 SQLite 扩展,允许在 SQLite 中存储和查询浮点、整数和二进制矢量。
它是 sqlite-vss 的继任者,旨在提供跨平台的矢量搜索能力,能够在各种环境下运行,包括 Linux、MacOS、Windows,甚至浏览器(使用 WASM)和树莓派等设备。
什么时候使用sqlite-vec,适用于哪些场景?
大多数现实世界的AI应用并不涉及数十亿个向量,而是处理数千或数百个向量。
最近邻(ANN)算法用于索引,但这会降低搜索效率。
sqlite-vec使用快速的暴力搜索,因此适合小型应用(或数据库)使用。
sqlite-vec有哪些优势?
sqlite-vec的几个优势包括:
- 无其他依赖
- 使用虚拟表
- 在阴影表中存储向量块
- 避免一次性加载所有内容到内存中
- 支持在 vec0 虚拟表中存储和查询浮点、int8 和二进制矢量。
- 使用纯 C 编写,无任何依赖项,能够在任何支持 SQLite 的环境中运行。
- 可通过 rowid IN (...) 子查询预筛选矢量。
安装
要在您的项目中使用 sqlite-vec,您可以根据您的开发环境选择相应的安装方式:
语言 | 安装命令 | 更多信息 |
Python | pip install sqlite-vec | PyPI |
Node.js | npm install sqlite-vec | npm |
Ruby | gem install sqlite-vec | Gem |
Go | go get -u github.com/asg017/sqlite-vec/bindings/go | Go Reference |
Rust | cargo add sqlite-vec | Crates.io |
Datasette | datasette install datasette-sqlite-vec | Datasette |
rqlite | rqlited -extensions-path=sqlite-vec.tar.gz | rqlite |
sqlite-utils | sqlite-utils install sqlite-utils-sqlite-vec | sqlite-utils |
入门示例
以下是如何使用 sqlite-vec 的简单示例:
以下是如何使用 sqlite-vec 的简单示例:
1、加载扩展:
.load ./vec0
2、创建虚拟表:
CREATE VIRTUAL TABLE vec_examples USING vec0(
sample_embedding FLOAT[8] -- 创建一个包含8维浮点向量的虚拟表
);
3、插入矢量:
您可以通过 JSON 格式或紧凑的二进制格式提供向量。以下是将几个矢量插入表的示例:
INSERT INTO vec_examples(rowid, sample_embedding)
VALUES
(1, '[-0.200, 0.250, 0.341, -0.211, 0.645, 0.935, -0.316, -0.924]'),
(2, '[0.443, -0.501, 0.355, -0.771, 0.707, -0.708, -0.185, 0.362]'),
(3, '[0.716, -0.927, 0.134, 0.052, -0.669, 0.793, -0.634, -0.162]'),
(4, '[-0.710, 0.330, 0.656, 0.041, -0.990, 0.726, 0.385, -0.958]');
4、执行 KNN 查询:
您可以执行 KNN 查询来查找与给定向量最接近的向量:
SELECT
rowid,
distance
FROM vec_examples
WHERE sample_embedding MATCH '[0.890, 0.544, 0.825, 0.961, 0.358, 0.0196, 0.521, 0.175]'
ORDER BY distance
LIMIT 2;
这将返回最接近的两个结果,查询结果类似于:
一个完整示例
假设我们想要创建一个在线书籍推荐系统。每本书都有一些特征,比如主题、风格和情感色彩等。我们将用这些特征来为用户推荐相似的书籍。
import sqlite3
import sqlite_vec
from typing import List
import struct
def serialize_f32(vector: List[float]) -> bytes:
"""将浮点数列表序列化为紧凑的原始字节格式"""
return struct.pack("%sf" % len(vector), *vector)
# 连接到内存数据库
db = sqlite3.connect(":memory:")
db.enable_load_extension(True)
sqlite_vec.load(db)
db.enable_load_extension(False)
# 查询SQLite和vec版本
sqlite_version, vec_version = db.execute(
"select sqlite_version(), vec_version()"
).fetchone()
print(f"sqlite_version={sqlite_version}, vec_version={vec_version}")
# 准备书籍数据,每本书有一个嵌入向量表示其特征
books = [
(1, [0.9, 0.8, 0.1, 0.2]), # 书籍 1 特征向量
(2, [0.7, 0.6, 0.2, 0.4]), # 书籍 2 特征向量
(3, [0.1, 0.1, 0.9, 0.8]), # 书籍 3 特征向量
(4, [0.3, 0.5, 0.7, 0.9]), # 书籍 4 特征向量
(5, [0.2, 0.4, 0.6, 0.5]), # 书籍 5 特征向量
]
# 用户喜欢的书籍特征向量
query = [0.1, 0.1, 0.9, 0.8] # 用户输入的书籍特征向量
# 创建虚拟表用于存储书籍信息
db.execute("CREATE VIRTUAL TABLE vec_books USING vec0(embedding float[4])")
# 插入书籍数据
with db:
for book in books:
db.execute(
"INSERT INTO vec_books(rowid, embedding) VALUES (?, ?)",
[book[0], serialize_f32(book[1])],
)
# 查询与用户喜欢的书籍相似的书籍
rows = db.execute(
"""
SELECT
rowid,
distance
FROM vec_books
WHERE embedding MATCH ?
ORDER BY distance
LIMIT 3
""",
[serialize_f32(query)],
).fetchall()
# 输出推荐的书籍
print("推荐的书籍 ID 和与用户输入书籍的距离:")
for row in rows:
print(row)
代码解读
1、数据准备:
每本书用一个浮点数列表表示其特征向量,向量的不同维度可以代表书籍的主题、风格和情感等。例如:
书籍 1:特征向量 [0.9, 0.8, 0.1, 0.2] 可能表示一本浪漫小说。
书籍 2:特征向量 [0.7, 0.6, 0.2, 0.4] 可能表示一本悬疑小说。
书籍 3:特征向量 [0.1, 0.1, 0.9, 0.8] 可能表示一本科幻小说。
2、查询相似书籍:
用户输入的书籍特征向量为 [0.1, 0.1, 0.9, 0.8],代表他们喜欢的书籍。
使用 sqlite_vec 扩展的 MATCH 功能查询与用户输入书籍相似的书籍,结果按照距离升序排列,并限制返回 3 条记录。
3、输出推荐结果:
最后打印推荐的书籍的 ID 和与用户输入书籍的距离。
输出:
这个代码实现了一个简单的书籍推荐系统,通过输入用户喜欢书籍的特征,查询与之相似的书籍,并推荐给用户。
这样的实现方式可以用在各种基于特征的推荐系统中,比如电影推荐、音乐推荐等。
知识拓展
Q: 什么是向量量化?它为什么重要?向量量化如何工作?
A: 向量量化是一种向量压缩技术。我们通过以下例子来理解:
每个浮点向量占用4字节的空间。如果我们需要存储100万个向量,每个向量有1500维,那么所需的存储空间为:
sqlite-vec支持位向量和浮点向量换句话说,在sqlite-vec中,一个向量可以用1位表示。因此,大小减少了32倍。
Q: 向量量化的后果是什么?
A: 向量量化或任何其他向量压缩技术会导致质量损失。根据嵌入模型的不同,准确率将降低约5-10%,但查询速度将提高约10倍。
结论
sqlite-vec 提供了一种简单、跨平台的方式来实现 SQLite 中的矢量搜索。
通过加载扩展、创建虚拟表、插入矢量并执行 KNN 查询,您可以在自己的应用程序中轻松集成这一强大功能。
这种能力能够显著提升数据检索的灵活性和准确性,特别是在处理自然语言处理和机器学习相关任务时。
https://alexgarcia.xyz/blog/2024/sqlite-vec-stable-release/index.html