RyanCodrai/turbovec큐레이터 픽

A vector index built on TurboQuant, written in Rust with Python bindings

GitHub에서 열기 ↗ annavx512embeddingembeddingsfaissnearest-neighborneonpythonquantquantizationragrustsimdturboquantvector-search
12,629
GitHub 스타
1,116
포크
Python
언어
MIT
라이선스
2026.06.10
최근 푸시
2026.05.24
별표한 날

AI 분석

설치 난이도: 쉬움
큐레이터 노트
turbovec는 메모리 효율성과 검색 속도가 중요한 RAG 시스템, 특히 프라이버시가 중요한 온프레미스 환경에 적합합니다. FAISS의 대안으로 고려할 가치가 있으며, 특히 ARM 기반 시스템에서 우수한 성능을 발휘합니다.

강점

  • TurboQuant 알고리즘을 사용하여 FAISS보다 높은 압축률과 경쟁력 있는 검색 속도를 제공합니다.
  • 별도의 학습 단계 없이 온라인 수집이 가능하여 사용이 간편합니다.
  • 검색 시 필터링을 SIMD 커널 내에서 효율적으로 처리하여 선택적 필터에서도 성능 저하가 적습니다.
  • Python, Rust, LangChain, LlamaIndex 등 다양한 프레임워크와의 통합을 지원합니다.

약점

  • x86 2비트 설정에서 FAISS AVX-512 VBMI 경로에 비해 검색 속도가 약간 느립니다.
  • 저차원 임베딩(예: GloVe d=200)에서는 점근적 베타 가정이 덜 정확하여 재현율이 상대적으로 낮을 수 있습니다.
  • 문서화된 벤치마크는 100K 벡터 규모에 국한되어 있어 대규모 데이터셋에서의 성능을 추가로 확인해야 합니다.

주의사항

  • AVX-512 커널은 런타임 감지되므로, 오래된 CPU에서는 AVX2 폴백으로 동작하여 성능이 저하될 수 있습니다.
  • TQ+ 보정은 첫 번째 추가 시에만 수행되므로, 이후 추가되는 데이터의 분포가 크게 다를 경우 재현율에 영향을 줄 수 있습니다.
  • IdMapIndex의 remove는 O(1)이지만, 내부 슬롯 관리 방식에 따라 메모리 단편화가 발생할 수 있습니다.

시작 가이드

  • pip install turbovec로 설치하고 기본 예제를 실행해 보세요.
  • 자신의 임베딩 모델과 데이터셋으로 재현율 및 속도 벤치마크를 수행해 보세요.
  • LangChain, LlamaIndex 등 사용 중인 프레임워크와의 통합 문서를 참고하여 적용해 보세요.
  • 대규모 데이터셋(100만 벡터 이상)에서의 메모리 사용량과 검색 지연 시간을 측정해 보세요.

README 한국어 번역

이 번역은 AI가 원문 README를 옮긴 것입니다. 원문이 항상 우선합니다.

turbovec는 Google Research의 TurboQuant 알고리즘을 기반으로 구축된 Rust 벡터 인덱스로, Python 바인딩을 제공합니다. TurboQuant는 데이터에 무관한(data-oblivious) 양자화기로, 별도의 학습 단계 없이 최적에 가까운 왜곡을 달성합니다.

  • 온라인 수집. 벡터를 추가하면 즉시 인덱싱됩니다. 학습 단계, 매개변수 튜닝, 코퍼스 증가에 따른 재구축이 필요 없습니다.
  • 빠른 SIMD 검색. 수제 NEON(ARM) 및 AVX-512BW(x86) 커널이 ARM에서 FAISS IndexPQFastScan보다 10–19% 빠릅니다. x86에서는 4비트 설정에서 우세하며, 2비트에서는 몇 퍼센트 뒤집니다.
  • 검색 시 필터링. search()에 ID 허용 목록(allowlist) 또는 슬롯 비트마스크를 전달하면 커널이 이를 직접 처리합니다. 항상 허용된 집합에서 최대 k개의 결과를 반환하며, 오버페칭이나 선택적 필터에 따른 재현율 저하가 없습니다.
  • 완전 로컬. 관리형 서비스가 필요 없으며, 데이터가 머신이나 VPC를 벗어나지 않습니다. 오픈소스 임베딩 모델과 결합하여 완전히 에어갭(air-gapped)된 RAG 스택을 구성할 수 있습니다.

Python

pip install turbovec
from turbovec import TurboQuantIndex

index = TurboQuantIndex(dim=1536, bit_width=4)
index.add(vectors)
index.add(more_vectors)

scores, indices = index.search(query, k=10)

index.write("my_index.tv")
loaded = TurboQuantIndex.load("my_index.tv")

삭제 후에도 유지되는 안정적인 ID가 필요하면 IdMapIndex를 사용하세요:

import numpy as np
from turbovec import IdMapIndex

index = IdMapIndex(dim=1536, bit_width=4)
index.add_with_ids(vectors, np.array([1001, 1002, 1003], dtype=np.uint64))

scores, ids = index.search(query, k=10)   # ids는 사용자의 uint64 외부 ID
index.remove(1002)                         # O(1) ID 기반 삭제

index.write("my_index.tvim")
loaded = IdMapIndex.load("my_index.tvim")

하이브리드 검색 (필터링된 검색)

다른 시스템(SQL, BM25, ACL, 시간 윈도우 등)에서 생성된 후보 집합으로 결과를 제한합니다:

import numpy as np
from turbovec import IdMapIndex

idx = IdMapIndex(dim=1536, bit_width=4)
idx.add_with_ids(vectors, ids)

# 1단계: 외부 시스템이 후보 ID로 좁힘.
allowed = np.array(db.execute("SELECT id FROM docs WHERE tenant=?", (t,)).fetchall(),
                   dtype=np.uint64)

# 2단계: 후보 집합 내에서 밀집 재순위화.
scores, ids = idx.search(query, k=10, allowlist=allowed)

필터링은 32-벡터 블록 단위로 SIMD 커널 내부에서 발생합니다. 허용된 슬롯이 없는 블록은 LUT 조회나 점수 계산 전에 조기 종료되고, 점수가 계산된 블록 내에서 허용되지 않은 개별 슬롯은 힙 삽입 시 제거됩니다. 선택적 허용 목록(인덱스의 작은 부분만 허용)은 SIMD 비용을 지불한 후 결과를 폐기하는 대신 대부분의 SIMD 비용을 회피합니다.

출력 길이는 min(k, len(allowed))입니다. 허용 목록이 k보다 작으면 패딩된 폴백 없이 정확히 len(allowed)개의 결과를 반환합니다.

전체 API 참조는 docs/api.md를 참조하세요.

프레임워크 통합

각 프레임워크의 기본 벡터/문서 저장소를 대체할 수 있는 드롭인(drop-in) 대체품입니다. 동일한 공개 인터페이스, 동일한 지속성 의미, 동일한 검색기 및 파이프라인 연결을 유지합니다. 임포트만 변경하면 파이프라인을 그대로 사용할 수 있습니다.

  • LangChain — pip install turbovec[langchain] · langchain_core.vectorstores.InMemoryVectorStore 대체
  • LlamaIndex — pip install turbovec[llama-index] · llamaindex.core.vector_stores.SimpleVectorStore 대체
  • Haystack — pip install turbovec[haystack] · haystack.documentstores.inmemory.InMemoryDocumentStore 대체
  • Agno — pip install turbovec[agno] · agno.vectordb.lancedb.LanceDb 대체

Rust

cargo add turbovec
use turbovec::TurboQuantIndex;

let mut index = TurboQuantIndex::new(1536, 4).unwrap();
index.add(&vectors);
let results = index.search(&queries, 10);
index.write("index.tv").unwrap();
let loaded = TurboQuantIndex::load("index.tv").unwrap();

삭제 후에도 유지되는 안정적인 외부 ID가 필요하면:

use turbovec::IdMapIndex;

let mut index = IdMapIndex::new(1536, 4).unwrap();
index.add_with_ids(&vectors, &[1001, 1002, 1003]).unwrap();
let (scores, ids) = index.search(&queries, 10);
index.remove(1002);
index.write("index.tvim").unwrap();
let loaded = IdMapIndex::load("index.tvim").unwrap();

재현율

TurboQuant 대 FAISS IndexPQ (LUT256, nbits=8) — 논문의 섹션 4.4 기준. 100K 벡터, k=64. FAISS PQ 하위 양자화기 개수는 TurboQuant의 비트 전송률과 일치하도록 설정 (2비트에서 m=d/4, 4비트에서 m=d/2).

!Recall GloVe d=200

!Recall d=1536

!Recall d=3072

OpenAI d=1536 및 d=3072에서 TurboQuant는 2비트와 4비트 모두에서 R@1 기준 FAISS보다 0.2–1.9 포인트 높으며, 둘 다 k=8에서 1.0에 도달합니다 (k=4에서 이미 ≥0.997). GloVe d=200은 더 어려운 영역입니다. 저차원에서는 점근적 베타 가정이 덜 정확합니다. TurboQuant는 4비트에서 FAISS보다 0.9 포인트 높고, 2비트에서는 R@1에서 사실상 동등하며(0.1 포인트 이내), 둘 다 k≈16에서 FAISS를 밀접하게 추적합니다.

기준에 대한 참고. FAISS IndexPQ (LUT256, nbits=8, float32 LUT)와 비교합니다. 이는 대부분의 사용자가 사용할 기본 프로덕션 등급 PQ이기 때문입니다. 이는 TurboQuant 논문의 사용자 정의 u8-LUT PQ보다 더 강력한 기준입니다. FAISS는 점수 계산 시 더 높은 정밀도의 LUT를 사용하고 코드북 학습에 k-means++를 사용합니다. OpenAI d=1536 / d=3072에서 논문의 TurboQuant 수치를 재현했으며, 저차원 임베딩(d=384에서 turboquant-py 참조)에서 다른 커뮤니티 참조 구현과 유사한 수치를 얻었습니다. GloVe (d=200) — 점근적 베타 가정이 가장 느슨한 저차원 영역 — 에서 TurboQuant는 2비트에서 FAISS와 동등하고 4비트에서 앞섭니다. TQ+ 보정은 기본 알고리즘이 남긴 저차원 격차를 줄입니다.

전체 결과: d=1536 2-bit, d=1536 4-bit, d=3072 2-bit, d=3072 4-bit, GloVe 2-bit, GloVe 4-bit.

압축

!Compression

검색 속도

모든 벤치마크: 100K 벡터, 1K 쿼리, k=64, 5회 실행 중앙값.

ARM (Apple M3 Max)

!ARM Speed — Single-threaded

!ARM Speed — Multi-threaded

ARM에서 TurboQuant는 모든 설정에서 FAISS FastScan보다 10–19% 빠릅니다.

x86 (Intel Xeon Platinum 8481C / Sapphire Rapids, 8 vCPUs)

!x86 Speed — Single-threaded

!x86 Speed — Multi-threaded

x86에서 TurboQuant는 4비트 설정에서 최대 ~5% 우세하며(d=3072 멀티스레드는 동률), 2비트에서는 FAISS에 약간 뒤집니다. 가장 눈에 띄는 것은 d=1536 단일 스레드(~8%)이며, 나머지는 몇 퍼센트 이내입니다. FAISS의 AVX-512 VBMI 경로가 짧은 2비트 누적 루프에서 우위를 점합니다.

작동 원리

각 벡터는 고차원 초구 위의 방향입니다. TurboQuant는 간단한 통찰을 사용하여 이러한 방향을 압축합니다. 무작위 회전을 적용한 후, 모든 좌표는 입력 데이터와 관계없이 알려진 분포를 따릅니다.

1. 정규화. 각 벡터에서 길이(노름)를 제거하고 단일 float로 저장합니다. 이제 모든 벡터는 초구 위의 단위 방향입니다.

2. 무작위 회전. 모든 벡터에 동일한 무작위 직교 행렬을 곱합니다. 회전 후, 각 좌표는 독립적으로 고차원에서 가우시안 N(0, 1/d)으로 수렴하는 베타 분포를 따릅니다. 이는 모든 입력 데이터에 대해 성립합니다. 회전은 좌표 분포를 예측 가능하게 만듭니다.

3. 좌표별 보정 (TQ+). 2단계의 베타 분포는 점근적입니다. 유한 차원에서는 개별 좌표가 표준 형태에서 벗어납니다(특히 저비트 및 단어 벡터 스타일 임베딩). TQ+는 첫 번째 추가 중에 각 좌표에 대해 두 개의 스칼라(이동 및 스케일)를 피팅하여 각 좌표의 경험적 5/95% 분위수를 표준 베타 주변 분포에 매핑합니다. 그런 다음 Lloyd-Max 코드북은 설계된 대상 분포에 대해 양자화합니다. 보정은 첫 번째 추가 후 고정되고 이후 추가에서 재사용됩니다. 재학습, 재구축, 별도의 학습 단계가 없습니다. 재현율 향상: 가장 많이 벗어나는 셀(예: 2비트 GloVe)에서 @1 기준 최대 +1.4pp.

4. Lloyd-Max 스칼라 양자화. 분포가 알려져 있으므로 각 좌표를 버킷팅하는 최적의 방법을 미리 계산할 수 있습니다. 2비트의 경우 4개 버킷, 4비트의 경우 16개 버킷입니다. Lloyd-Max 알고리즘은 평균 제곱 오차를 최소화하는 버킷 경계와 중심을 찾습니다. 이는 데이터가 아닌 수학에서 한 번 계산됩니다.

5. 비트 패킹. 각 좌표는 이제 작은 정수(2비트의 경우 0-3, 4비트의 경우 0-15)입니다. 이를 바이트에 촘촘히 패킹합니다. 1536차원 벡터는 6,144바이트(FP32)에서 384바이트(2비트)로 줄어듭니다. 이는 16배 압축입니다.

6. 길이 재정규화 점수 계산. 스칼라 양자화는 내적을 체계적으로 과소평가합니다. 재구성된 단위 방향은 원래보다 약간 짧습니다. 인코딩 시 각 벡터에 대해 하나의 스칼라(회전된 단위 벡터와 자체 중심 재구성의 내적)를 계산하고 ||v|| / ⟨u, x̂⟩를 각 압축된 벡터와 함께 저장합니다. 검색 커널은 힙 삽입 전에 후보별 점수에 이 스칼라를 곱하여 내적 추정기를 하향 편향에서 무편향으로 전환합니다. 검색 시간 비용이나 추가 저장 공간이 없습니다. 재현율 향상은 양자화 수축이 가장 큰 저비트 폭에서 가장 두드러집니다.

인코딩 비용: ⟨u, x̂⟩를 계산하기 위한 벡터당 하나의 추가 d차원 내적. d=1536에서 100만 벡터의 경우 인코딩 시간이 1초 미만으로 추가됩니다. 이는 쿼리 시가 아닌 수집 시 지불하는 일회성 비용입니다.

검색. 모든 데이터베이스 벡터를 압축 해제하는 대신, 쿼리를 동일한 도메인으로 회전시키고 코드북 값에 대해 직접 점수를 계산합니다. 점수 계산 커널은 니블 분할 룩업 테이블을 사용하여 SIMD 내장 함수(NEON on ARM, 최신 x86에서 AVX-512BW, AVX2 폴백)를 사용하여 최대 처리량을 달성합니다.

Lloyd-Max 코드북은 정보 이론적 하한(Shannon의 왜곡-율 한계)의 2.7배 이내의 왜곡을 달성합니다. 길이 재정규화 단계는 Lloyd-Max 코드북이 내적 추정기 자체에 도입하는 잔여 편향을 제거합니다.

빌드

Python (maturin 사용)

pip install maturin
cd turbovec-python
maturin build --release
pip install target/wheels/*.whl

Rust

cargo build --release

모든 x8664 빌드는 .cargo/config.toml을 통해 x86-64-v3 (AVX2 기준, Haswell 2013+)을 대상으로 합니다. AVX2 폴백 커널을 실행할 수 있는 모든 CPU는 전체 크레이트를 실행할 수 있습니다. AVX-512 커널은 런타임에 isx86featuredetected!를 통해 게이트되며, 이를 지원하는 하드웨어에서만 활성화됩니다.

벤치마크 실행

데이터셋 다운로드:

python3 benchmarks/d

원본 저장소: RyanCodrai/turbovec

라이선스: MIT

게재 제외를 원하시면 삭제 요청을 보내주세요.