一 为什么会有LanceDB?
LanceDB 是一个用于人工智能的开源向量数据库,旨在存储、管理、查询和检索大规模多模式数据的嵌入。LanceDB 的核心是用Rust编写的,并构建在Lance之上,Lance 是一种开源列式数据格式,专为高性能 ML 工作负载和快速随机访问而设计。Lance的性能比parquet高2000倍。
数据库和底层数据格式都经过精心设计,易于使用、可扩展且经济高效。
二 LanceDB的使用方法
LanceDB 可以通过多种方式运行:
- 嵌入现有后端(例如 Django、Flask、Node.js 或 FastAPI 应用程序)
- 直接从客户端应用程序(例如 Jupyter 笔记本)连接以进行分析工作负载
- 部署为远程无服务器数据库
2.1 安装
pip install lancedb
2.2 链接到数据库
import lancedb
uri = "data/sample-lancedb"
db = lancedb.connect(uri)
2.3 创建表格
tbl = db.create_table("my_table",
data=[{"vector": [3.1, 4.1], "item": "foo", "price": 10.0},
{"vector": [5.9, 26.5], "item": "bar", "price": 20.0}])
如果表已经存在,LanceDB默认会抛出错误。如果你想覆盖表,你可以传入mode="overwrite" 该create_table方法。
您还可以直接传入 pandas DataFrame:
import pandas as pd
df = pd.DataFrame([{"vector": [3.1, 4.1], "item": "foo", "price": 10.0},
{"vector": [5.9, 26.5], "item": "bar", "price": 20.0}])
tbl = db.create_table("table_from_df", data=df)
2.4 创建一个空表
import pyarrow as pa
schema = pa.schema([pa.field("vector", pa.list_(pa.float32(), list_size=2))])
tbl = db.create_table("empty_table", schema=schema)
2.5 打开现有表
tbl = db.open_table("my_table")
如果您忘记了表的名称,您始终可以获得所有表名称的列表:
print(db.table_names())
2.6 向表中添加数据
# Option 1: Add a list of dicts to a table
data = [{"vector": [1.3, 1.4], "item": "fizz", "price": 100.0},
{"vector": [9.5, 56.2], "item": "buzz", "price": 200.0}]
tbl.add(data)
# Option 2: Add a pandas DataFrame to a table
df = pd.DataFrame(data)
tbl.add(data)
2.7 相似性搜索
tbl.search([100, 100]).limit(2).to_pandas()
2.8 从表中删除数据
tbl.delete('item = "fizz"')
2.9 删除表
db.drop_table("my_table")
三 LanceDB的原理
3.1 向量索引
向量搜索是一种基于向量表示搜索相似项目的技术,称为嵌入。它也称为相似性搜索、最近邻搜索或近似最近邻搜索。
原始数据(例如文本、图像、音频等)通过嵌入模型转换为嵌入,然后存储在像 LanceDB 这样的矢量数据库中。为了大规模执行相似性搜索,在存储的嵌入上创建索引,然后可以使用该索引来执行快速查找。
现代机器学习模型可以经过训练将原始数据转换为嵌入,表示为固定维度的浮点数数组(或向量)。嵌入在实践中有用之处在于,嵌入在向量空间中的位置捕获了数据的一些语义,具体取决于模型的类型及其训练方式。向量空间中彼此靠近的点被认为是相似的(或出现在相似的上下文中),而远离的点被认为是不相似的。
多模式数据(文本、音频、图像等)的大型数据集可以使用适当的模型转换为嵌入。将向量的主成分投影到 2D 空间中会产生表示相似概念的向量组,这些向量聚集在一起,如下所示。
3.2 IVF-PQ索引
ANN(近似最近邻)索引是一种数据结构,它以提高搜索和检索效率的方式表示数据。使用 ANN 索引速度更快,但不如 kNN 或强力搜索准确,因为本质上索引是数据的有损表示。
LanceDB 与其他矢量数据库有根本的不同,因为它构建在Lance之上,Lance 是一种开源列式数据格式,专为高性能 ML 工作负载和快速随机访问而设计。由于Lance的设计,LanceDB的索引理念主要采用基于磁盘的索引理念。
IVF-PQ是倒排文件索引(IVF)和乘积量化(PQ)相结合的复合索引。LanceDB 中的实现提供了几个参数来微调索引的大小、查询吞吐量、延迟和召回,本节稍后将对此进行描述。
乘积量化 (PQ) 的工作原理是将一个大的高维向量划分为大小相等的子向量。每个子向量都分配有一个“再现值”,该值映射到该子向量的最近的点质心。然后将再现值分配给使用唯一 ID 的码本,该码本可用于重建原始向量。
重要的是要记住,量化是一个有损过程,即重建的向量与原始向量不同。这导致索引的大小和搜索结果的准确性之间的权衡。
例如,考虑从由 32 位浮点数组成的 128 维向量开始。将其量化为 4 维的 8 位整数向量(如上图所示),我们可以显着减少内存需求。
原始:128 × 32 = 4096位 量化:4 × 8 = 32位
量化使索引中每个向量的内存需求减少了128 倍,这是相当可观的。
3.3 倒排索引
虽然 PQ 有助于减小索引的大小,但 IVF 主要解决搜索性能问题。倒排文件索引的主要目的是通过缩小搜索空间来促进快速有效的最近邻居搜索。
在 IVF 中,PQ 向量空间被划分为Voronoi 单元,这些单元本质上是由空间中距给定区域种子点阈值距离内的所有点组成的分区。这些种子点用于创建倒排索引,将每个质心与空间中的向量列表相关联,从而允许搜索仅限于索引中向量的子集。
在查询期间,根据查询在向量空间中的位置,它可能靠近多个 Voronoi 单元的边界,这可能会使 top-k 结果不明确并跨越多个单元。为了解决这个问题,IVF-PQ 引入了参数nprobe,该参数控制查询期间要搜索的 Voronoi 单元的数量。越高nprobe,结果越准确,但查询速度越慢。
我们可以结合上面的概念来理解如何在LanceDB中构建和查询IVF-PQ索引。
3.4 构建索引&查询索引
构建索引
构建 IVF-PQ 索引时需要设置三个关键参数:
- metric:使用L2欧氏距离度量。我们也支持dot并cosine保持距离。
- num_partitions:索引的 IVF 部分中的分区数。
- num_sub_vectors:在乘积量化 (PQ) 期间将创建的子向量的数量。
在Python中,可以按如下方式创建索引:
# Create and train the index for a 1536-dimensional vector
# Make sure you have enough data in the table for an effective training step
tbl.create_index(metric="L2", num_partitions=256, num_sub_vectors=96)
通常选择num_partitions针对每个分区特定数量的向量。num_sub_vectors通常根据所需的召回率和向量的维数来选择。
查询索引
# Search using a random 1536-dimensional embedding
tbl.search(np.random.random((1536))) \
.limit(2) \
.nprobes(20) \
.refine_factor(10) \
.to_pandas()
上面的查询将使用给定的查询向量对表执行搜索tbl,参数如下:
- limit: 返回结果数
- nprobes:探针的数量决定了向量空间的分布。虽然较高的数字可以提高搜索准确性,但也会导致性能下降。通常,设置nprobes覆盖数据集的 5-10% 可以有效地以最小的延迟实现高召回率。
- refine_factor:通过读取额外元素并在内存中重新排列它们来优化结果。数字越大,搜索越准确,但速度也越慢。
- to_pandas():将结果转换为pandas DataFrame
四 性能 - Lance VS Parquet
Lance 提供与 Parquet 相当的扫描性能,但支持快速随机访问,使其非常适合:搜索引擎、实时特征检索、加快深度学习训练的洗牌性能
在这里,我们将比较 Lance 与 Parquet 的随机访问性能。我们将创建 1 亿条记录,其中每个值都是随机生成的 1000 个字符长的字符串。然后,我们运行 1000 个查询的基准测试,在数据集中随机获取 20-50 行。两个测试都是在同一个 Ubuntu 22.04 系统上完成的:
sudo lshw -short
类描述
================================================ =================
系统 20M9CTO1WW (LENOVO_MT_20M9_BU_Think_FM_ThinkPad P52)
内存 128GiB 系统内存
32GiB SODIMM DDR4 同步 2667 MHz (0.4 ns)
内存 32GiB SODIMM DDR4 同步 2667 MHz (0.4 ns)
内存32GiB SODIMM DDR4 同步 2667 MHz (0.4 ns)
内存 32GiB SODIMM DDR4 同步 2667 MHz (0.4 ns)
内存 384KiB L1 高速
缓存 1536KiB L2 高速
缓存 12MiB L3 高速缓存
处理器 Intel(R) Xeon(R) E-2176M CPU @ 2.70GHz
存储三星 SSD 980 PRO 2TB
还使用 LMDB 对类似的设置进行了基准测试,并将所有内容绘制在同一张图表上以进行比较:
LanceDB一直声称随机访问性能比 Parquet快100 倍,但正如该基准测试所示,它实际上更像是2000倍。Lance 为重要的ML工作流程所需的 OSS 数据生态系统带来了快速随机访问性能。这对于训练深度学习模型的搜索、特征水合和洗牌至关重要。虽然 Lance 的性能对于这些用例来说已经非常有价值,但我们将努力实现通用键查找、更好的 duckdb 集成以及在 Spark/Ray 节点之间分发大型 Lance 数据集的挂钩。
五 LanceDB的应用场景
- 嵌入式(OSS)和无服务器(云),无需管理服务器
- 快速生产规模向量相似性、全文和混合搜索以及 SQL 查询接口(通过DataFusion)
- 原生 Python 和 Javascript/Typescript 支持
- 存储、查询和管理多模式数据(文本、图像、视频、点云等),而不仅仅是嵌入和元数据
- 与Arrow生态系统紧密集成,通过 SIMD 和 GPU 加速实现共享内存中真正的零复制访问
- 自动数据版本控制可管理数据版本,无需额外的基础设施
- 基于磁盘的索引和存储,无需花费大量资金即可实现大规模可扩展性
- 直接摄取您最喜欢的数据格式,例如 pandas DataFrames、Pydantic 对象、Polars(即将推出)等
参考资料
[1] github源码:https://github.com/lancedb/lancedb
[2] Lance 中的随机访问基准测试:https://blog.lancedb.com/benchmarking-random-access-in-lance-ed690757a826
[3] LanceDB wiki:https://lancedb.github.io/lancedb
[4] LanceDB quick start:https://lancedb.github.io/lancedb/basic
[5] LanceDB concepts:https://lancedb.github.io/lancedb/concepts/vector_search