Zum Inhalt
wannacode

Suchen nach Bedeutung

Kurz gesagt: Die Vektorsuche wandelt Text in einen Vektor aus Zahlen (ein Embedding) um und findet die Datensätze, die der Bedeutung nach am nächsten liegen, über die Kosinus-Ähnlichkeit.

Problem

„Etwas zum Frühstück“

Ein Kunde tippt in die Suche des Shops „etwas zum Frühstück“ ein. Das Wort steht in keinem Produktnamen:

SQLDatenbank: Shop

Die Volltextsuche aus der vorigen Lektion findet nur die Bewertung „Das Vollkornbrot ist wunderbar zum Frühstück.“ Dabei sind Milch, ein Brötchen, Kaffee und Banitsa genau das, was der Kunde will. Er sucht nach Bedeutung, nicht nach einem Wort. Synonyme, Übersetzungen und „etwas zum …“ fängt man mit Wörtern nicht.

Die Idee

Ein Text als Punkt

Ein Sprachmodell (ein Embedding-Modell) wandelt einen Text in einen Vektor um: eine Liste von Zahlen. Es ist so trainiert, dass Texte mit naher Bedeutung nahe Vektoren ergeben. „Milch“, „Brötchen“ und „etwas zum Frühstück“ liegen nah beieinander, und „Waschpulver“ weit weg.

Echte Modelle liefern von ein paar hundert bis zu ein paar tausend Zahlen, und keine davon hat einen Namen. Damit du siehst, was passiert, sind die Vektoren hier ausgedacht und haben nur vier benannte Dimensionen: Frühstück, süß, Kochen und Reinigen. Die Tabelle product_vectors hält den Vektor jedes Produkts als JSON-Array:

SQLDatenbank: Shop

Das Brötchen ist sehr „Frühstück“ und ein wenig „süß“. Die Kartoffeln sind nur „Kochen“ und das Waschpulver nur „Reinigen“.

Genauer, für Neugierige

Der Vektor wird einmal berechnet, wenn das Produkt hinzukommt oder sich seine Beschreibung ändert, und in der Datenbank gehalten. Die Frage des Kunden wird bei jeder Suche mit demselben Modell in einen Vektor umgewandelt. Die beiden Vektoren müssen vom selben Modell stammen: Vektoren verschiedener Modelle lassen sich nicht vergleichen. Es gibt offene Modelle (zum Beispiel aus der Familie sentence-transformers, die auf einem gewöhnlichen Computer laufen) und bezahlte Dienste.

Die Idee

Wie nah: der Kosinus

Zwei Vektoren sind nah, wenn sie in dieselbe Richtung zeigen. Das Maß ist die Kosinus-Ähnlichkeit: 1 heißt dieselbe Richtung, 0 heißt nichts gemeinsam. Sie wird so berechnet: Multipliziere die Zahlen an denselben Stellen und addiere sie (das Skalarprodukt), dann teile durch die Längen der beiden Vektoren.

json_each entfaltet ein JSON-Array zu Zeilen: key ist die Stelle und value die Zahl. Der Weißkäse gegen die Frage „etwas zum Frühstück“ ([1, 0.2, 0.1, 0], so wie das Modell sie liefern würde):

SQLDatenbank: Shop

Die Summe der letzten Spalte ist 0,66. Geteilt durch die Längen (0,849 für den Käse und 1,025 für die Frage) ergibt das 0,759. Dasselbe für alle Produkte, vom nächsten abwärts:

SQLDatenbank: Shop

WITH q(v) liefert den Vektor der Frage. Die zwei json_each werden über die Stelle verbunden, und GROUP BY addiert die Produkte für jeden Eintrag. Keines dieser Produkte hat das Wort „Frühstück“ im Namen.

Probier es aus

Etwas Süßes zum Frühstück

Das Modell hat die Frage „etwas Süßes zum Frühstück“ in den Vektor [0.7, 0.9, 0, 0] umgewandelt. Finde die drei nächsten Produkte (die Spalten name und similarity, auf 0.001 gerundet), vom nächsten abwärts.

SQLDatenbank: Shop

Die Idee

Filtern vor der Sortierung

Die Suche nach Bedeutung hat oft auch gewöhnliche Bedingungen: unter einem Preis, nur auf Lager, nur in einer Kategorie. Die Bedingung gehört in WHERE, also vor GROUP BY, ORDER BY und LIMIT. Nimmst du erst die nächsten drei und filterst dann, kann nichts übrig bleiben: Es sind Kaffee für 14,90 €, Banitsa für 2,50 € und Milch für 1,60 €.

Genauer, für Neugierige

Vektorindizes (die nächste Karte) haben genau dieses Problem. Die ungefähre Suche liefert zum Beispiel die nächsten 100, und kommt der Filter danach, während die günstigen Produkte nicht unter diesen 100 sind, ist das Ergebnis leer. Deshalb haben Vektordatenbanken Filter, die während der Suche angewendet werden, und pgvector hat Einstellungen wie hnsw.ef_search und iterative Scans (hnsw.iterative_scan).

Übung

Günstig und ähnlich

1. Der Kunde hat „bis 1,50 €“ gewählt.

Finde die drei Produkte, die „etwas zum Frühstück“ ([1, 0.2, 0.1, 0]) am nächsten liegen, mit einem Preis unter 1,50 € (die Spalten name und similarity), vom nächsten abwärts.

SQLDatenbank: Shop

2. Die Produktseite zeigt „ähnliche Produkte“. Der Vektor der Frage ist der Vektor des Produkts selbst.

Finde die drei Produkte, die dem Weißkäse (Produkt 7) am ähnlichsten sind, ohne den Käse selbst (die Spalten name und similarity), vom nächsten abwärts.

SQLDatenbank: Shop

Die Idee

Millionen Vektoren: ein ungefährer Index

Die Abfrage vergleicht die Frage mit jedem Produkt. Der Plan zeigt SCAN p:

SQLDatenbank: Shop

Bei 23 Produkten mit je 4 Zahlen ist das nichts. Bei 10 Millionen Dokumenten mit je 1536 Zahlen ist jede Suche 15 Milliarden Multiplikationen. Ein gewöhnlicher Index hilft nicht: Er sortiert nach einem Wert, während „nah“ bei Vektoren in hunderten Richtungen gleichzeitig gilt.

Deshalb nutzen Vektordatenbanken ungefähre Indizes (ANN, approximate nearest neighbours). Am weitesten verbreitet ist HNSW: ein Graph, in dem jeder Vektor auf mehreren Ebenen mit seinen nahen verbunden ist. Die Suche beginnt auf der obersten, dünnen Ebene und geht nach unten, immer zu näheren Nachbarn. Sie findet die nächsten in Millisekunden, verpasst aber manchmal einen davon. Der Anteil der wirklich nächsten, den der Index findet, ist der Recall, und er wird gegen die Geschwindigkeit abgestimmt.

Die Idee

Wo die Vektoren leben

Am einfachsten ist es, wenn die Vektoren neben den Daten liegen. pgvector ist eine Erweiterung von PostgreSQL:

CREATE EXTENSION vector;
CREATE TABLE product_vectors (
product_id integer PRIMARY KEY REFERENCES products(id),
embedding vector(384)
);
CREATE INDEX ON product_vectors USING hnsw (embedding vector_cosine_ops);
-- <=> ist die Kosinus-Distanz (1 minus die Ähnlichkeit)
SELECT p.name
FROM product_vectors pv JOIN products p ON p.id = pv.product_id
WHERE p.price < 1.5
ORDER BY pv.embedding <=> $1
LIMIT 3;

Eigene Vektordatenbanken (Qdrant, Weaviate, Milvus, Pinecone) lohnen sich bei hunderten Millionen Vektoren oder wenn die Suche nach Bedeutung die Hauptaufgabe ist. Auch Elasticsearch, OpenSearch und Redis haben eine Vektorsuche.

Der häufigste Einsatz heute ist RAG (retrieval-augmented generation): Der Chatbot des Shops wandelt die Frage in einen Vektor um, findet die nächsten Stücke aus Beschreibungen, Bewertungen und Regeln und gibt sie dem Sprachmodell, damit es daraus antwortet und nicht aus dem Gedächtnis. Die Suche nach Bedeutung wird oft mit der Volltextsuche gemischt: Die Wörter fangen genaue Namen und Codes, und die Vektoren die Bedeutung.

Test

Teste dich

1. Was ist ein Embedding?
2. Die Kosinus-Ähnlichkeit zweier Vektoren ist 1. Was bedeutet das?
3. Du suchst die drei nächsten Produkte unter 1,50 €. Warum wird der Preisfilter nicht nach LIMIT 3 angewendet?
4. Was bringt ein HNSW-Index in einer Vektordatenbank?

Erstellt von Ivo Gergov im Rahmen der Kampagne von Evo Tech für zugängliche Programmierausbildung ·