벡터라이트 소개: SQLite를 위한 빠르고 조정 가능한 벡터 검색 확장

WBOY
풀어 주다: 2024-07-19 13:01:02
원래의
1205명이 탐색했습니다.

Introducing vectorlite: A Fast and Tunable Vector Search Extension for SQLite

소개

LLM(Large Language Models)과 RAG(Retrieval-Augmented Generation)의 등장으로 Milvus, Pinecone과 같은 벡터 데이터베이스가 주목을 받고 있습니다. 또한 기존 데이터베이스는 PostgreSQL용 pgVector 및 SQLite용 sqlite-vss와 같은 타사 확장을 통해 벡터 검색 지원을 따라잡고 있습니다.

이 기사에서는 제가 방금 첫 번째 베타 버전을 출시한 SQLite용으로 작성한 또 다른 빠르고 조정 가능한 벡터 검색 확장 프로그램인 벡터라이트를 소개합니다. 이제 pip를 사용하여 설치할 수 있습니다

pip install vectorlite-py
로그인 후 복사

SQLite 확장은 런타임 시 SQLite에 의해 로드될 수 있는 동적 라이브러리입니다. 다음은 SQLite CLI 셸에서 벡터라이트를 사용하는 예입니다.

-- Load vectorlite
.load path/to/vectorlite.[so|dll|dylib]
-- shows vectorlite version and build info.
select vectorlite_info(); 
-- Calculate vector l2(squared) distance
select vector_distance(vector_from_json('[1,2,3]'), vector_from_json('[3,4,5]'), 'l2');
-- Create a virtual table named my_table with one vector column my_embedding with a dimention of 3 using default HNSW parameters.
create virtual table my_table using vectorlite(my_embedding float32[3], hnsw(max_elements=100));
-- Insert vectors into my_table. rowid can be used to relate to a vector's metadata stored elsewhere, e.g. another table.
insert into my_table(rowid, my_embedding) values (0, vector_from_json('[1,2,3]'));
insert into my_table(rowid, my_embedding) values (1, vector_from_json('[2,3,4]'));
insert into my_table(rowid, my_embedding) values (2, vector_from_json('[7,7,7]'));
-- Find 2 approximate nearest neighbors of vector [3,4,5] with distances
select rowid, distance from my_table where knn_search(my_embedding, knn_param(vector_from_json('[3,4,5]'), 2));
-- Find the nearest neighbor of vector [3,4,5] among vectors with rowid 0 and 1. (requires sqlite_version>=3.38)
-- It is called metadata filter in vectorlite, because you could get rowid set beforehand based on vectors' metadata and then perform vector search.
-- Metadata filter is pushed down to the underlying index when traversing the HNSW graph.
select rowid, distance from my_table where knn_search(my_embedding, knn_param(vector_from_json('[3,4,5]'), 1)) and rowid in (0, 1) ;

로그인 후 복사

전체 API 참조는 여기에서 확인하세요.

하이라이트

  1. hnswlib가 지원하는 빠른 ANN(Approximate Nearest Neighbors) 검색. 벤치마크를 참조하세요.
  2. Windows, Linux, MacOS에서 작동합니다.
  3. Vector_distance()를 사용하여 x86 플랫폼에 대한 SIMD 가속 벡터 거리 계산
  4. hnswlib에서 제공하는 모든 벡터 거리 유형(l2(제곱 l2), cosine, ip(내적. 하지만 사용을 권장하지 않음))을 지원합니다. 자세한 내용은 hnswlib 문서를 확인하세요.
  5. 성능 튜닝을 위해 HNSW 매개변수를 완벽하게 제어합니다. 이 예시를 확인해 보세요.
  6. 벡터 메타데이터 필터에 대한 조건자 푸시다운 지원(sqlite 버전 >= 3.38 필요) 이 예시를 확인해 보세요.
  7. 색인 Serde를 지원합니다. 벡터라이트 테이블은 파일에 저장하고 파일에서 다시 로드할 수 있습니다. hnswlib로 생성된 인덱스 파일은 벡터라이트로도 로드할 수 있습니다. 이 예시를 확인해 보세요.
  8. Vector_from_json() 및 vector_to_json()을 사용하여 벡터 json serde를 지원합니다.

SQLite를 위한 또 다른 벡터 검색 확장이 필요한 이유는 무엇입니까?

우선, SQLite는 가볍고 데이터를 로컬에 저장하여 데이터를 훨씬 안전하게 만들어주기 때문에 LLM 기반 앱을 위한 귀중한 도구입니다.

SQLite에 대한 벡터 검색을 가능하게 하는 sqlite-vss가 이미 있지만 그에 대한 기술적 결정 중 일부는 토론할 가치가 있으므로 Vectorlite를 작성합니다. sqlite-vss의 작성자는 다른 벡터 검색 확장 프로젝트로 이동하여 자신의 관점에서 sqlite-vss의 문제점을 설명하는 기사를 작성했습니다. 내 것을 공유하겠습니다.

벡터 검색 라이브러리의 선택

Sqlite-vss는 faiss를 사용하여 벡터 검색을 수행합니다. Meta(facebook)에서 오픈소스로 제공하는 훌륭한 라이브러리이며 벡터 검색을 위한 광범위한 알고리즘을 제공합니다. 그러나 대규모 데이터세트에 대한 일괄 작업에 최적화되어 있어 단일 벡터 쿼리 및 CPU의 증분 인덱싱에는 속도가 느려집니다. 그러나 SQLite의 확장성 모델(가상 테이블이라고 함)은 일괄 작업을 위한 API를 제공하지 않으며 한 번에 하나의 행을 삽입/업데이트/삭제하는 API만 노출합니다. 게다가 sqlite-vss는 단일 벡터 검색만 지원하는데, 이는 faiss가 좋지 않습니다. 결과적으로 sqlite-vss는 faiss의 성능을 완전히 활용하지 못합니다.

Vectorlite는 HNSW 알고리즘의 빠른 구현을 제공하고 증분 인덱스 구성 및 단일 벡터 쿼리에 최적화된 hnswlib를 사용하며 이는 SQLite의 가상 테이블 API와 잘 작동합니다.

내 벤치마크에서 sqlite-vss와 비교했을 때 벡터라이트는 벡터 삽입 속도가 10배 더 빠르고 검색 속도가 2배~40배 더 빠릅니다(속도-정확도 균형을 갖춘 HNSW 매개변수에 따라 다름). 이는 주로 벡터 검색 라이브러리 선택이 다르기 때문입니다. .

벡터 메타데이터 필터

sqlite-vss가 놓친 또 다른 중요한 기능은 벡터 메타데이터 필터링입니다. 실제 시나리오에서 벡터는 항상 날짜, 장르, 카테고리, 이름, 유형, 내용 등을 포함한 일부 메타데이터로 태그가 지정됩니다. 벡터 검색을 수행하기 전에 태그를 기반으로 벡터를 필터링하는 것은 의심의 여지가 없습니다. 기능적 벡터 데이터베이스.

Vectorlite는 첫 번째 출시 이후 벡터 메타데이터 필터(sqlite 버전 >= 3.38 필요)를 지원합니다. 예시를 확인해주세요.

프로덕션 배포를 위한 성능 조정

sqlite-vss와 vectorlite는 모두 벡터 검색에 ANN(Approximate Nearest Neighbors) 알고리즘을 활용합니다. 즉, 결과가 100% 정확하다고 보장할 수는 없지만 검색 속도는 상대적으로 빠릅니다. 생산 과정에서 ANN 알고리즘의 성능은 워크로드, 임베딩 및 특정 요구 사항에 따라 벤치마킹되고 조정되어야 합니다. 예를 들어, 어떤 사람은 실시간 의미 검색 앱을 구축하기 때문에 더 낮은 재현율로 빠른 벡터 쿼리가 필요할 수도 있고, 다른 사람은 높은 재현율이 필요하지만 일부 오프라인 분석을 수행하기 때문에 검색 속도에 신경 쓰지 않을 수도 있습니다.

In a typical recommendation system, its vector index is often built and tuned offline using libraries like faiss and hnswlib for acceptable speed and accuracy, and then served in production.

With sqlite-vss, you have to build vector index using it, making performance tuning very difficult. One cannot build the vector index offline with faiss and then serve the index with sqlite-vss. Though you do get to pass faiss strings to it to tune the index, non-default indexes are so slow that they barely work (probably due to the nature of the algorithm or improper use of faiss I mentioned above).

With vectorlite, you have full control over the HNSW paramters to tune the search speed and recall rate. Please check this example and the result.

┏━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━┳━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━┓
┃ distance ┃ vector    ┃ ef           ┃    ┃ ef     ┃ insert_time ┃ search_time ┃ recall ┃
┃ type     ┃ dimension ┃ construction ┃ M  ┃ search ┃ per vector  ┃ per query   ┃ rate   ┃
┡━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━╇━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━┩
│ l2       │ 256       │ 200          │ 32 │ 10     │ 291.13 us   │ 35.70 us    │ 31.60% │
│ l2       │ 256       │ 200          │ 32 │ 50     │ 291.13 us   │ 99.50 us    │ 72.30% │
│ l2       │ 256       │ 200          │ 32 │ 100    │ 291.13 us   │ 168.80 us   │ 88.60% │
│ l2       │ 256       │ 200          │ 32 │ 150    │ 291.13 us   │ 310.53 us   │ 95.50% │
│ l2       │ 256       │ 200          │ 48 │ 10     │ 286.92 us   │ 37.79 us    │ 37.30% │
│ l2       │ 256       │ 200          │ 48 │ 50     │ 286.92 us   │ 117.73 us   │ 80.30% │
│ l2       │ 256       │ 200          │ 48 │ 100    │ 286.92 us   │ 196.01 us   │ 93.80% │
│ l2       │ 256       │ 200          │ 48 │ 150    │ 286.92 us   │ 259.88 us   │ 98.20% │
│ l2       │ 256       │ 200          │ 64 │ 10     │ 285.82 us   │ 50.26 us    │ 42.60% │
│ l2       │ 256       │ 200          │ 64 │ 50     │ 285.82 us   │ 138.83 us   │ 84.00% │
│ l2       │ 256       │ 200          │ 64 │ 100    │ 285.82 us   │ 253.18 us   │ 95.40% │
│ l2       │ 256       │ 200          │ 64 │ 150    │ 285.82 us   │ 316.45 us   │ 98.70% │
│ l2       │ 1024      │ 200          │ 32 │ 10     │ 1395.02 us  │ 158.75 us   │ 23.50% │
│ l2       │ 1024      │ 200          │ 32 │ 50     │ 1395.02 us  │ 564.27 us   │ 60.30% │
│ l2       │ 1024      │ 200          │ 32 │ 100    │ 1395.02 us  │ 919.26 us   │ 79.30% │
│ l2       │ 1024      │ 200          │ 32 │ 150    │ 1395.02 us  │ 1232.40 us  │ 88.20% │
│ l2       │ 1024      │ 200          │ 48 │ 10     │ 1489.91 us  │ 252.91 us   │ 28.50% │
│ l2       │ 1024      │ 200          │ 48 │ 50     │ 1489.91 us  │ 848.13 us   │ 69.40% │
│ l2       │ 1024      │ 200          │ 48 │ 100    │ 1489.91 us  │ 1294.02 us  │ 86.80% │
│ l2       │ 1024      │ 200          │ 48 │ 150    │ 1489.91 us  │ 1680.97 us  │ 94.20% │
│ l2       │ 1024      │ 200          │ 64 │ 10     │ 1412.03 us  │ 273.36 us   │ 33.30% │
│ l2       │ 1024      │ 200          │ 64 │ 50     │ 1412.03 us  │ 899.13 us   │ 75.50% │
│ l2       │ 1024      │ 200          │ 64 │ 100    │ 1412.03 us  │ 1419.61 us  │ 90.10% │
│ l2       │ 1024      │ 200          │ 64 │ 150    │ 1412.03 us  │ 1821.85 us  │ 96.00% │
│ cosine   │ 256       │ 200          │ 32 │ 10     │ 255.22 us   │ 28.66 us    │ 38.60% │
│ cosine   │ 256       │ 200          │ 32 │ 50     │ 255.22 us   │ 85.39 us    │ 75.90% │
│ cosine   │ 256       │ 200          │ 32 │ 100    │ 255.22 us   │ 137.31 us   │ 91.10% │
│ cosine   │ 256       │ 200          │ 32 │ 150    │ 255.22 us   │ 190.87 us   │ 95.30% │
│ cosine   │ 256       │ 200          │ 48 │ 10     │ 259.62 us   │ 57.31 us    │ 46.60% │
│ cosine   │ 256       │ 200          │ 48 │ 50     │ 259.62 us   │ 170.54 us   │ 84.80% │
│ cosine   │ 256       │ 200          │ 48 │ 100    │ 259.62 us   │ 221.11 us   │ 94.80% │
│ cosine   │ 256       │ 200          │ 48 │ 150    │ 259.62 us   │ 239.90 us   │ 97.90% │
│ cosine   │ 256       │ 200          │ 64 │ 10     │ 273.21 us   │ 49.34 us    │ 48.10% │
│ cosine   │ 256       │ 200          │ 64 │ 50     │ 273.21 us   │ 139.07 us   │ 88.00% │
│ cosine   │ 256       │ 200          │ 64 │ 100    │ 273.21 us   │ 242.51 us   │ 96.30% │
│ cosine   │ 256       │ 200          │ 64 │ 150    │ 273.21 us   │ 296.21 us   │ 98.40% │
│ cosine   │ 1024      │ 200          │ 32 │ 10     │ 1192.27 us  │ 146.86 us   │ 27.40% │
│ cosine   │ 1024      │ 200          │ 32 │ 50     │ 1192.27 us  │ 451.61 us   │ 66.10% │
│ cosine   │ 1024      │ 200          │ 32 │ 100    │ 1192.27 us  │ 826.40 us   │ 83.30% │
│ cosine   │ 1024      │ 200          │ 32 │ 150    │ 1192.27 us  │ 1199.33 us  │ 90.00% │
│ cosine   │ 1024      │ 200          │ 48 │ 10     │ 1337.96 us  │ 200.14 us   │ 33.10% │
│ cosine   │ 1024      │ 200          │ 48 │ 50     │ 1337.96 us  │ 654.35 us   │ 72.60% │
│ cosine   │ 1024      │ 200          │ 48 │ 100    │ 1337.96 us  │ 1091.57 us  │ 88.90% │
│ cosine   │ 1024      │ 200          │ 48 │ 150    │ 1337.96 us  │ 1429.51 us  │ 94.50% │
│ cosine   │ 1024      │ 200          │ 64 │ 10     │ 1287.88 us  │ 257.67 us   │ 38.20% │
│ cosine   │ 1024      │ 200          │ 64 │ 50     │ 1287.88 us  │ 767.61 us   │ 77.00% │
│ cosine   │ 1024      │ 200          │ 64 │ 100    │ 1287.88 us  │ 1250.36 us  │ 92.10% │
│ cosine   │ 1024      │ 200          │ 64 │ 150    │ 1287.88 us  │ 1699.57 us  │ 96.50% │
└──────────┴───────────┴──────────────┴────┴────────┴─────────────┴─────────────┴────────┘
로그인 후 복사

The same benchmark is also run for sqlite-vss using its default index:

┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┓
┃ vector dimension ┃ insert_time(per vector) ┃ search_time(per query) ┃ recall_rate ┃
┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━┩
│ 256              │ 3644.42 us              │ 1483.18 us             │ 55.00%      │
│ 1024             │ 18466.91 us             │ 3412.92 us             │ 52.20%      │
└──────────────────┴─────────────────────────┴────────────────────────┴─────────────┘
로그인 후 복사

Vectorlite is not only much faster, but also offers much better recall rate if properly tuned. Besides, you can also build the index using hnswlib directly and serve the index using vectorlite as vectorlite can be considered a thin wrapper around hnswlib with a SQL API.

Quick start

The quickest way to get started is to install vectorlite using python.

# Note: vectorlite-py not vectorlite. vectorlite is another project.
pip install vectorlite-py apsw numpy
로그인 후 복사

Vectorlite's metadata filter feature requires sqlite>=3.38. Python's builtin sqlite module is usually built with old sqlite versions. So apsw is used here as the SQLite driver, as it provides bindings to newer SQLite. Vectorlite still works with old SQLite versions if metadata filter support is not required.
Below is a minimal example of using vectorlite.

import vectorlite_py
import apsw
import numpy as np
"""
Quick start of using vectorlite extension.
"""

conn = apsw.Connection(':memory:')
conn.enable_load_extension(True) # enable extension loading
conn.load_extension(vectorlite_py.vectorlite_path()) # load vectorlite

cursor = conn.cursor()
# check if vectorlite is loaded
print(cursor.execute('select vectorlite_info()').fetchall())

# Vector distance calculation
for distance_type in ['l2', 'cosine', 'ip']:
    v1 = "[1, 2, 3]"
    v2 = "[4, 5, 6]"
    # Note vector_from_json can be used to convert a JSON string to a vector
    distance = cursor.execute(f'select vector_distance(vector_from_json(?), vector_from_json(?), "{distance_type}")', (v1, v2)).fetchone()
    print(f'{distance_type} distance between {v1} and {v2} is {distance[0]}')

# generate some test data
DIM = 32 # dimension of the vectors
NUM_ELEMENTS = 10000 # number of vectors
data = np.float32(np.random.random((NUM_ELEMENTS, DIM))) # Only float32 vectors are supported by vectorlite for now

# Create a virtual table using vectorlite using l2 distance (default distance type) and default HNSW parameters
cursor.execute(f'create virtual table my_table using vectorlite(my_embedding float32[{DIM}], hnsw(max_elements={NUM_ELEMENTS}))')
# Vector distance type can be explicitly set to cosine using:
# cursor.execute(f'create virtual table my_table using vectorlite(my_embedding float32[{DIM}] cosine, hnsw(max_elements={NUM_ELEMENTS}))')

# Insert the test data into the virtual table. Note that the rowid MUST be explicitly set when inserting vectors and cannot be auto-generated.
# The rowid is used to uniquely identify a vector and serve as a "foreign key" to relate to the vector's metadata.
# Vectorlite takes vectors in raw bytes, so a numpy vector need to be converted to bytes before inserting into the table.
cursor.executemany('insert into my_table(rowid, my_embedding) values (?, ?)', [(i, data[i].tobytes()) for i in range(NUM_ELEMENTS)])

# Query the virtual table to get the vector at rowid 12345. Note the vector needs to be converted back to json using vector_to_json() to be human-readable. 
result = cursor.execute('select vector_to_json(my_embedding) from my_table where rowid = 1234').fetchone()
print(f'vector at rowid 1234: {result[0]}')

# Find 10 approximate nearest neighbors of data[0] and there distances from data[0].
# knn_search() is used to tell vectorlite to do a vector search.
# knn_param(V, K, ef) is used to pass the query vector V, the number of nearest neighbors K to find and an optional ef parameter to tune the performance of the search.
# If ef is not specified, ef defaults to 10. For more info on ef, please check https://github.com/nmslib/hnswlib/blob/v0.8.0/ALGO_PARAMS.md
result = cursor.execute('select rowid, distance from my_table where knn_search(my_embedding, knn_param(?, 10))', [data[0].tobytes()]).fetchall()
print(f'10 nearest neighbors of row 0 is {result}')

# Find 10 approximate nearest neighbors of the first embedding in vectors with rowid within [1000, 2000) using metadata(rowid) filtering.
rowids = ','.join([str(rowid) for rowid in range(1000, 2000)])
result = cursor.execute(f'select rowid, distance from my_table where knn_search(my_embedding, knn_param(?, 10)) and rowid in ({rowids})', [data[0].tobytes()]).fetchall()
print(f'10 nearest neighbors of row 0 in vectors with rowid within [1000, 2000) is {result}')

conn.close()

로그인 후 복사

More examples can be found in examples and integration_test folder.

Conclusion

Vectorlite is created with the hope that it could be the go-to vector search solution for SQLite like pgvector for PostgreSQL.
It is still in early stage. Any suggestions are welcome and appreciated.

위 내용은 벡터라이트 소개: SQLite를 위한 빠르고 조정 가능한 벡터 검색 확장의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:dev.to
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿