Neue Vektorsuch-Erweiterung sqlite-vec für SQLite in Entwicklung
(alexgarcia.xyz)- Um die Einschränkungen des bestehenden
sqlite-vsszu reduzieren, wirdsqlite-vecals rein C-basierte eingebettete Vektorsuch-Erweiterung entwickelt und zielt auf praktisch alle Umgebungen ab, in denen SQLite läuft - Der SQL-Nutzungsfluss wird auf
CREATE VIRTUAL TABLE,INSERT INTOundSELECTvereinfacht und unterstützt KNN-Suche im Stil von KNN sowie Vektoreingaben als JSON und kompaktes Binary-Format - Ohne Faiss-Abhängigkeit zielt es neben Linux und macOS auch auf Windows, WebAssembly, Mobile und Raspberry Pi ab; die Binärgröße wird statt der bisherigen 3 MB bis 5 MB im Bereich von einigen Hundert KB erwartet
- Vektoren werden in Chunks in Shadow Tables gespeichert, wodurch weniger Bedarf besteht, alles in den RAM zu laden; mit
PRAGMA mmap_sizelässt sich die Geschwindigkeit speicherbasierter Suchen erhöhen - Die erste Version bietet nur exhaustive Full-Scan und noch kein ANN; ein Release von
v0.1.0ist geplant, nachdem die 246 TODOs insqlite-vec.cerledigt sind
Was sqlite-vec an der Vektorsuche in SQLite ändern will
sqlite-vecist eine neue, in reinem C geschriebene SQLite-Erweiterung und ein Projekt, das das im Februar 2023 veröffentlichtesqlite-vssersetzen soll- Der Zielumfang umfasst benutzerdefinierte SQL-Funktionen für schnelle Vektorsuche, Virtual Tables sowie Tools und Utilities für Vektoroperationen
- Quantisierung
- Umwandlung zwischen JSON/BLOB/numpy
- Vektorarithmetik
- Nutzer können einen Vektorspeicher ausschließlich mit SQL erstellen und durchsuchen
- Erstellen einer Virtual Table für Vektoren mit
CREATE VIRTUAL TABLE - Einfügen von Vektoren mit
INSERT INTO - KNN-Suche in der Form
SELECT ... WHERE sample_embedding MATCH ... ORDER BY distance LIMIT ...
- Erstellen einer Virtual Table für Vektoren mit
- Als Vektoreingabe kann ein JSON-String oder ein kompaktes Binary-Format verwendet werden
Reines C und keine Abhängigkeiten erweitern die Laufzeitumgebungen
sqlite-veczielt auf eine abhängigkeitsfreie Erweiterung in reinem C ab; diese Entscheidung ist die zentrale Voraussetzung für eine breitere Plattformunterstützung- Das bestehende
sqlite-vsslief wegen seiner C++-Abhängigkeit nur unter Linux und macOS zuverlässig, und die Binärgröße lag im Bereich von 3 MB bis 5 MB - Die neue Erweiterung soll in folgenden Umgebungen laufen
- Linux
- macOS
- Windows
- WebAssembly im Browser
- Mobile Geräte
- Kleine Geräte wie Raspberry Pi
- Die erwartete Binärgröße liegt im Bereich von einigen Hundert KB
Speicherverbrauch und Suchgeschwindigkeit steuern
sqlite-vecsteuert den Speicherverbrauch, indem Vektoren in Chunks innerhalb einer Shadow Table gespeichert werden- Bei einer KNN-Suche werden nicht alle Vektoren auf einmal in den RAM geladen, sondern chunkweise gelesen
- Die gesamten Vektoren müssen nicht dauerhaft im Speicher liegen
- Wenn speicherbasierte Geschwindigkeit benötigt wird, kann SQLite-
PRAGMA mmap_sizegenutzt werden, um KNN-Suchen schneller zu machen
Neue Vektorfunktionen und anfängliche Einschränkungen
sqlite-vecwill folgende Funktionen besser unterstützen und damit aktuelle Vektorsuch-Tools und Forschungstrends aufgreifen- Embeddings mit adaptiver Länge, also Matryoshka Embeddings
int8- undbit-Vektoren- Binary and Scalar Quantization
- Diese Funktionen bilden die Grundlage, um Geschwindigkeit, Genauigkeit und Speicherplatzbedarf auf der Festplatte von Vektoren feiner zu steuern
- Die frühe Version von
sqlite-vecunterstützt nur Vektorsuche per exhaustive Full-Scan- Eine Option für „approximate nearest neighbors“ gibt es anfangs nicht
- IVF und HNSW sind Funktionen, die künftig ergänzt werden sollen
Aufbau der Browser-Demo
- Die Demo nutzt
sqlite-vec, das im Browser läuft - In den Developer Tools ist folgende Konfiguration zu sehen
- ein nicht optimiertes 5,9 MB großes
sqlite3.wasm - der offizielle SQLite-WASM-Build, in den
sqlite-veckompiliert wurde - eine 2,6 MB große SQLite-Datenbank
movies.bit.db
- ein nicht optimiertes 5,9 MB großes
movies.bit.dbenthält in der Tabellearticles4.800 Film-Overviews auf Basis der TMDB-Filmmetadaten- Eine separate Virtual Table
vec_moviesist der Vektorindex für die Embeddings dieser Overviews- Für die Embeddings wird das Nomic 1.5 Embeddings Model verwendet
- Die Vektoren werden als Binary Vector quantisiert
Demo-Daten und Ablauf der KNN-Suche
- Die Tabelle
articlesenthält Spalten wietitle,release_dateundoverview - Die Spalte
overviewenthält kurze Sätze zur Filmhandlung und dient in der Demo als Ziel für Embeddings - Die Virtual Table
vec_moviesspeichert die Embeddings vonarticles.overviewin der Spalteoverview_embeddings- Die Vektoren sind 768-dimensionale Binary Vectors
- Die Speichergröße beträgt
768 / 8 = 96, also 96 Byte
- Wenn der Nutzer per Radio-Button einen Film auswählt, wird die ausgewählte Film-ID in den Parameter
:selected_movieder KNN-SQL-Query eingesetzt - Die Suchergebnisse sind die 10 Filme, die dem ausgewählten Film am nächsten liegen
- Da es sich um Binary Vectors handelt, wird zur Distanzberechnung die Hamming Distance verwendet
- Das nächstgelegene Ergebnis ist immer derselbe Film, mit Distanz 0
- Die Ergebnisse der Einbettung kurzer Ein-Satz-Handlungen und eines kleinen Filmdatensatzes sind nicht von höchster Qualität, und Binary Quantization opfert zusätzlich Qualität; der Fokus liegt aber darauf, eine schnelle und „gut genug“ Vektorsuche im Browser zu zeigen
- Um das interne Verhalten zu prüfen, kann man
EXPLAIN QUERY PLANvorSELECTsetzen; dann sieht man den vonvec_moviesverwendeten0:knn-„Index“
Einschränkungen von sqlite-vss und die Faiss-Abhängigkeit
- Bei Entwicklung und Nutzung von
sqlite-vssgab es mehrere Hindernisse- Es läuft nur unter Linux und macOS und unterstützt weder Windows, WASM noch mobile Geräte
- Es speichert alle Vektoren im Speicher
- Es gibt Bugs und Issues im Zusammenhang mit Transaktionen
- Die Kompilierung ist sehr schwierig und dauert lange
- Gängige Vektoroperationen wie Scalar/Binary Quantization fehlen
- Fast alle diese Probleme gehen auf die Abhängigkeit von
Faisszurück - Einige Probleme könnten sich mit viel Zeit und Aufwand lösen lassen, viele andere könnten jedoch durch Faiss blockiert sein
- Eine abhängigkeitsfreie Low-Level-Lösung wurde zu einer attraktiven Option, und aus der Einschätzung heraus, dass Vektorsuche selbst nicht besonders komplex ist, entstand
sqlite-vec
Release-Status und Sponsoring-Aufruf
- Die Kernfunktionen von
sqlite-vecfunktionieren, aber Fehlerbehandlung und Tests sind noch sehr unzureichend - In der Datei
sqlite-vec.csind noch 246 TODOs offen- 191-mal
todo_assert() - 41
// TODO-Kommentare - 14-mal
todo panic - Der Gesamtfortschritt wird für
sqlite-vec v0mit 0/246, 0 % angezeigt
- 191-mal
- Wenn die 246 TODOs erledigt sind, soll das erste Release
v0.1.0erscheinen- Dokumentation
- Demos
- Bindings
- weitere Komponenten sollen mitgeliefert werden
- Der Zielzeitpunkt liegt bei ungefähr einem Monat, ist aber kein fester Termin
- Gesucht werden Sponsoren aus Unternehmen, die am Erfolg von
sqlite-vecinteressiert sind; Anfragen sind per E-Mail möglich
1 Kommentare
Meinungen auf Hacker News
Ich bin der Autor — wenn es Fragen gibt, beantworte ich sie gern. Das ist eher ein „ich arbeite an einem neuen Projekt“ als ein offizielles Release, und die Erweiterung selbst ist noch in Arbeit. Der Projektlink ist https://github.com/asg017/sqlite-vec
Ich habe schon eine ziemlich konkrete Vorstellung davon, wie v0.1.0 dieser Erweiterung aussehen wird, aber bis dahin dürfte es noch ein paar Wochen dauern. Dieser Beitrag sollte vor allem Nutzer meiner früheren SQLite-Erweiterung für Vektorsuche, sqlite-vss, darüber informieren, was als Nächstes kommt; sobald es bereit ist, wird es ein größeres Release geben.
Insgesamt freue ich mich sehr darauf, eine leicht einzubettende Alternative für Vektorsuche zu haben. Besonders gut ist, dass sie auf allen Betriebssystemen, in WASM, auf Mobilgeräten, Raspberry Pi usw. läuft; persönlich versuche ich gerade, auf dem Beepy eine kleine semantische Such-App laufen zu lassen, was ziemlich spannend ist.
[0] https://beepy.sqfmi.com/
Außerdem würde ich gern die Performance im Vergleich zu sqlite-vss kennen. Mich interessieren Profiling-Zahlen sowohl zur Abfragegeschwindigkeit als auch zum Speicherverbrauch.
Insgesamt sieht das wirklich hervorragend aus, und mir gefällt diese Richtung.
Der Ansatz, dass sqlite-vec zunächst nur Vektorsuche per vollständigem Brute-Force-Scan unterstützt und keine ANN-Optionen (Approximate Nearest Neighbor) anbietet, später aber IVF und HNSW hinzufügen möchte, ist meiner Meinung nach zu 1000 % richtig. Mir gefällt, dass es nicht von Anfang an übermäßig komplex gemacht wird.
Ich habe schon einmal On-Device-Vektorsuche ausgeliefert, und mit der Kombination aus 128-Bit-Binärvektoren und Hamming-Distanz war es schnell genug, um selbst bei Datenbanken mit mehr als 200.000 Einträgen bei jedem Kameraframe eine vollständige Brute-Force-Distanzsuche auszuführen. Selbst auf günstigen Smartphones kamen über 10 fps heraus, auf guten Smartphones lief es sehr flüssig. Es ist überraschend oft der Fall, dass Brute Force ausreicht.
Wenn jedoch ANN-Algorithmen wie HNSW implementiert werden, wäre es großartig, wenn sich das im Tabellenindex-Paradigma abbilden ließe. Dann wäre der Wechsel von Brute-Force-Suche zu ANN so einfach wie das Anlegen eines Index auf einer Tabelle, und Experimente mit verschiedenen ANN-Algorithmen und Parametern wären über die Anpassung der Index-Erstellungsparameter möglich. Vielleicht geht es ohnehin schon in diese Richtung, aber ich wollte es vorsichtshalber erwähnen.
Außerdem frage ich mich, ob es zusammen mit sqlite-httpvfs gebaut wird. Das würde gut zu diesem Projekt passen: https://github.com/phiresky/sql.js-httpvfs
Mich würde interessieren, ob ihr erwogen habt, es für eine gemeinsame SQL-Vektor-DSL syntaxkompatibel mit pgvector zu machen. Ich vermute, die Nachteile wären deutlich kleiner als die Vorteile, aber ich frage mich, ob es machbar ist.
https://observablehq.com/@asg017/introducing-sqlite-loadable...
sqlite-vss wird bereits in einigen Projekten gut genutzt.
Der Teil „ein binärer Vektor mit 768 Dimensionen belegt 96 Byte (768 / 8 = 96)“ verwirrt mich. Genau hier liegt meiner Ansicht nach der Fluch der Dimensionalität, mit dem die meisten Vektorspeicher zu kämpfen haben, und das ist ein Problem noch vor der Indexierung.
Ich dachte zunächst, gemeint seien vielleicht 768 Dimensionen * 8 Byte (f64), also 6144 Byte. Üblicherweise reduziert man das unter Inkaufnahme eines gewissen Qualitätsverlusts auf f32 oder f16 oder eine noch kleinere Darstellung.
Falls es eine Methode gibt, 768 Dimensionen per Kompression oder einer trie-ähnlichen amortisierten Technik in 96 Byte unterzubringen, würde ich gern in einem eigenen Beitrag mehr darüber hören. Wenn jede Dimension als 1 Bit behandelt wird, verstehe ich es, aber dann habe ich weiterhin Fragen zur Suchqualität.
Einige Embedding-Modelle, etwa nomic v1.5[0] oder das neue Modell von mixedbread[1], werden speziell so trainiert, dass die Qualität auch nach binärer Quantisierung erhalten bleibt. Das gilt nicht für alle Modelle, daher können die Ergebnisse variieren. Im Allgemeinen scheint es bei sehr großen Vektoren, wie OpenAIs großem Embedding-Modell mit 3072 Dimensionen, bis zu einem gewissen Grad zu funktionieren, selbst wenn sie nicht speziell dafür trainiert wurden.
[0] https://twitter.com/nomic_ai/status/1769837800793243687
[1] https://www.mixedbread.ai/blog/binary-mrl
Wenn man für die Daten einen FAISS-Index verwendet und Product Quantization anwendet, kann man bei binären Merkmalen binäre Vektoren etwa mit PQ768x1 ausprobieren und auch Varianten vergleichen, bei denen jedes Vektorpaar auf einen von vier Werten quantisiert wird: https://github.com/facebookresearch/faiss/wiki/The-index-fac...
In Vektordatenbanken werden Daten vor dem Speichern üblicherweise in einen niedriger dimensionalen Raum komprimiert oder projiziert, wodurch sich diese Situation eher verbessert.
Dank sqlite-vss konnte ich lernen, wie RAG funktioniert, und es in einem Spielzeugprojekt implementieren. Das Debugging war etwas schwierig, aber wenn man es richtig hinbekommen hat, lief es unter Ubuntu einwandfrei, und ich nutze es immer noch
Freut mich, dass eine bessere neue Version ohne eingeschränkte Abhängigkeiten gebaut wird
Mich würde interessieren, ob geplant ist, nur die öffentliche SQLite API zu verwenden, oder ob eher eine Anbindung an die SQLite-Amalgamation vorgesehen ist
An so einer Funktion bin ich definitiv interessiert, muss mir aber überlegen, wie ich sie bei Wasm-basierten Go-Bindings getrennt von SQLite ausliefern kann. Bisher habe ich den gesamten C-Code gebündelt ausgeliefert, weil das deutlich einfacher war als Wasm-„Dynamic Linking“
Außerdem wurde inkrementelle BLOB-Ein-/Ausgabe erwähnt; wie ihr wahrscheinlich schon wisst, sollte man im Hinterkopf behalten, dass große BLOBs als verkettete Liste von Pages gespeichert werden, weshalb BLOB-I/O keineswegs Random Access ist
Ich mag die wazero-SQLite-Bindings wirklich sehr. Tatsächlich plane ich, 1) CGO-Bindings für sqlite-vec und 2) einen eigenen WASI-Build von sqlite-vec bereitzustellen, der direkt mit go-sqlite3 verwendet werden kann. Ursprünglich wollte ich das Build-Skript dieses Repositories verwenden, um die Datei sqlite3.wasm zu erzeugen. Wenn ihr es direkt im Projekt unterstützen wollt, müsste es meiner Einschätzung nach reichen, die Dateien sqlite-vec.c/h in go-sqlite3/sqlite3 zu legen
Inkrementelle BLOB-Ein-/Ausgabe habe ich auf die harte Tour gelernt. Sie ist bei der Query-Geschwindigkeit von sqlite-vec eindeutig ein limitierender Faktor. Wenn man die Chunk-Größe relativ klein hält, im niedrigen MB-Bereich, und page_size erhöht, war die Balance ganz okay, aber gerade page_size hat Nebenwirkungen. PRAGMA mmap_size hilft ebenfalls sehr, weil es die Pages offenbar im Speicher hält und Overflow-Lookups schneller macht, erhöht aber natürlich den Speicherverbrauch deutlich. Eine schwierige Balance
DuckDB hat heute die Erweiterung „Vector Similarity Search in DuckDB“ angekündigt
https://duckdb.org/2024/05/03/vector-similarity-search-vss.h...
Mit DuckDB VSS könnte man offenbar Embeddings erstellen, sie im DuckDB-Format speichern und dann innerhalb des CDNs SQL ausführen
Ich mag Projekte dieser Art. Mir gefällt, dass es ein Open-Source-Projekt ist, das auf ein sehr spezifisches Problem abzielt
Ich überlege immer wieder, ob sich im TypeScript/Next.js/React-Ökosystem etwas sehr Nützliches für eine technische Nische bauen ließe, aber mir ist noch keine Inspiration gekommen
Wir haben in der AI-RAG-App https://github.com/rnadigital/agentcloud für Ende-zu-Ende-Automatisierung die Qdrant-Vektordatenbank verwendet, und ich freue mich, dass ein Nachfolger entsteht. Mich würde interessieren, wann sie einsatzbereit sein wird und ob es einen Quickstart-Guide gibt
Ich könnte wohl auch beim Schreiben des Blogs helfen
Es gibt ein undokumentiertes
sqlite-vec-pip-Paket; wenn ihr es direkt aus dem „Agent Backend“ in Python aufrufen wollt, könntet ihr es vermutlich schon jetzt ausprobierenDas kommt ziemlich nah an das heran, was ich mir unter „README-driven Development“ vorgestellt habe. Ich frage mich, ob der Autor mit der Dokumentation angefangen hat
Allerdings ist sie in dem Zustand, in dem „80 % mit 20 % des Aufwands“ entstanden sind; die restlichen 20 %, also Fehlerbehandlung, Fuzz-Tests und Korrektheitstests, werden wohl 80 % der Zeit beanspruchen. Da Leute aber bereits Fragen zum aktuellen Zustand von
sqlite-vssstellen, dachte ich, dieser Blogpost „in Arbeit“ könnte einige dieser Fragen beantwortenDie Idee, mit der Dokumentation anzufangen, gefällt mir auch. Gerade bei SQLite-Erweiterungen ist es wirklich wichtig, wie die SQL API aussieht, also skalare Funktionen, virtuelle Tabellen und so weiter. Bevor ich den Großteil des Codes geschrieben habe, habe ich ziemlich viel skizziert, wie der SQL-Teil von sqlite-vec aussehen sollte
[0] https://github.com/asg017/sqlite-vec/blob/main/sqlite-vec.c
Das scheint ziemlich genau eine Antwort auf ein GitHub-Issue zu sein, das ich vor ein paar Monaten bei SQLite-vss erstellt habe. Genau genommen ist es keine Antwort auf dieses Issue, aber trotzdem
https://github.com/asg017/sqlite-vss/issues/124
Tatsächlich hat mich dieses Ticket beim ersten Lesen in das Rabbit Hole „Wie kann man sqlite-vss besser machen?“ geführt, und am Ende wurde daraus „Ich sollte sqlite-vec bauen“. Danke, dass du mich auf diesen Weg gebracht hast
Mit der eingebauten binären Quantisierung von sqlite-vec könntest du so etwas ungefähr folgendermaßen machen:
CREATE VIRTUAL TABLE vec_files USING vec0 ( contents_embedding bit[1536] );INSERT INTO vec_files(rowid,contents_embedding) VALUES ((1, vec_quantize_binary( /* 1536-dimension float vector here*/)))Ich frage mich, ob sqlite-vec bei der Ausführung im Browser Daten im browsernativen IndexedDB persistent speichern kann. Oder ob Nutzer diesen Teil selbst übernehmen müssen
Auch wenn die Antwort lautet, dass ihr darüber noch nicht nachgedacht habt, wäre ich dankbar, wenn ihr eure Überlegungen in diese Richtung teilen könntet
Ich bin mir nicht sicher, ob IndexedDB konkret unterstützt wird, aber localStorage/OPFS VFS können verwendet werden
[0] https://sqlite.org/wasm/doc/trunk/persistence.md#kvvfs