Suchen Sie “Welpe” in einem System, das nur Stichwörter abgleicht, und ein Dokument über Hundetraining taucht nicht auf, obwohl jeder, der beide liest, sie als eng verwandt bezeichnen würde. Stichwortsuche vergleicht Zeichenketten. Sie weiß nicht, dass “Welpe” und “Hund” fast dasselbe bedeuten. Embeddings lösen genau das. Sie verwandeln einen Text in eine Liste von Zahlen, einen Vektor, der so platziert wird, dass Texte mit ähnlicher Bedeutung nah beieinander landen, unabhängig davon, welche Wörter verwendet werden.
Wie ein Embedding-Modell Text in Zahlen umwandelt
Ein Embedding-Modell ist ein neuronales Netz, das darauf trainiert ist, Text auf einen Punkt in einem hochdimensionalen Raum abzubilden: 384, 768 oder 1536 Dimensionen sind gängige Größen, je nach Modell. Es schlägt keine Wörter in einem Wörterbuch nach. Aus riesigen Textmengen hat es gelernt, welche Wörter und Phrasen in ähnlichen Kontexten auftauchen, und kodiert das in der Position des Vektors.
Die Distanz in diesem Raum folgt der Bedeutungsähnlichkeit. “König” und “Königin” landen nah beieinander. “König” und “Golden Retriever” landen weit entfernt. Niemand hat dem Modell gesagt, diese Wörter zu vergleichen; die Geometrie entsteht aus dem Training, und der Vergleich ergibt sich daraus, wo jedes Wort landet.
Sie können dieselbe Form mit handgewählten Zahlen sehen, noch bevor ein echtes Modell ins Spiel kommt. Geben Sie jedem Wort einen Vektor aus 3 Zahlen (ein echtes Modell nutzt Hunderte):
| |
king und queen stimmen bei den ersten beiden Zahlen eng überein. dog nicht. Um “stimmen eng überein” in eine einzelne Kennzahl zu verwandeln, brauchen Sie eine Ähnlichkeitsmetrik.
Cosine Similarity, Dot Product und Euclidean Distance im Vergleich
Drei Metriken tauchen in der Embedding-Arbeit immer wieder auf, und jede beantwortet eine leicht andere Frage:
| Metrik | Was sie misst | Wann Sie sie verwenden |
|---|---|---|
| Cosine Similarity | Den Winkel zwischen zwei Vektoren, ohne deren Länge | Standard für Text-Embeddings, da die meisten Modelle dafür trainiert sind |
| Dot Product | Winkel und Länge zusammen | Das Modell wurde explizit für Dot-Product-Ranking trainiert (manche Retrieval-Modelle sind das) |
| Euclidean Distance | Luftlinienabstand zwischen den beiden Punkten | Räumliche oder zählbasierte Daten; bei Text seltener |
Cosine Similarity ist diejenige, zu der Sie fast immer greifen. Viele Text-Embedding-Modelle normalisieren ihre Ausgabe auf Länge 1, sodass nur die Richtung variiert, und bei normalisierten Vektoren liefern Cosine und Dot Product dasselbe Ranking. Der Wert reicht von -1 (entgegengesetzt) bis 1 (identisch), wobei 0 keinen Zusammenhang bedeutet. So sieht das bei den handgewählten Vektoren aus:
| |
0,801 gegen 0,375. king liegt deutlich näher an queen als an dog. Ein echtes Modell erzeugt dieselbe Art von Abstand, verteilt über Hunderte von Dimensionen, die es aus Daten gelernt hat statt aus drei handgewählten Zahlen.
Wie Sie Embeddings in Python erzeugen
sentence-transformers ist die Standard-Open-Source-Bibliothek dafür. Sie läuft lokal, ohne API-Key:
| |
| |
all-MiniLM-L6-v2 verwandelt jeden Satz in einen Vektor aus 384 Zahlen, und model.similarity berechnet die Cosine Similarity für jedes Paar. Die beiden Sätze über sonniges Wetter teilen sich fast kein Wort, erzielen aber trotzdem einen hohen Wert zueinander und einen niedrigen gegenüber dem Satz über das Stadion. Dasselbe Muster wie im Spielzeugbeispiel, nur dass die Zahlen jetzt von einem Modell stammen, das Sprache gelernt hat.
Gehostete APIs liefern dasselbe. Die Endpunkte text-embedding-3-small von OpenAI und embed von Cohere geben beide eine Liste von Gleitkommazahlen pro Text zurück. Wenn Sie bereits lokal Modelle betreiben, liefert auch Ollama Embeddings: ollama pull nomic-embed-text, dann rufen Sie dessen /api/embed-Endpunkt auf (wie das geht, zeigt Wie man ein LLM lokal mit Ollama ausführt). Die Bibliothek wechselt. Der Vektor und das, was Sie damit tun, bleiben gleich.
Wie die Vektor-Ähnlichkeitssuche die nächste Übereinstimmung findet
Das Embedding für einen einzelnen Text zu berechnen ist der einfache Teil. Die eigentliche Aufgabe ist fast immer: Finde zu einem Query-Vektor die nächsten Übereinstimmungen unter Tausenden oder Millionen gespeicherter Vektoren. Das ist die Vektor-Ähnlichkeitssuche, und die direkte Variante ist Brute Force: die Query gegen jeden gespeicherten Vektor bewerten und die besten Ergebnisse behalten.
| |
Auf einer gewöhnlichen Maschine gibt das etwa Folgendes aus:
| |
Die Zeit wächst linear mit der Anzahl der Vektoren, O(n) im Sinne der Big-O-Notation, aber NumPy macht jeden Schritt so günstig, dass eine Million Vektoren deutlich unter 100 ms benötigt. Das reicht nicht mehr aus bei starkem gleichzeitigem Traffic, Dutzenden von Millionen Vektoren oder beidem. Dann brauchen Sie einen Approximate-Nearest-Neighbor-Index (ANN), der für Vektoren das leistet, was ein B-Tree für eine WHERE-Klausel in einer Datenbank leistet (siehe Wie Datenbankindizes Funktionieren und Wann Sie Nutzen). Zwei Arten tauchen überall auf:
- HNSW (Hierarchical Navigable Small World) baut einen mehrschichtigen Graphen, der jeden Vektor mit seinen Nachbarn verbindet, sodass eine Suche sich durch den Graphen zur Antwort bewegt, statt jeden Vektor zu berühren. Es bietet den besten Recall pro Millisekunde unter den gängigen Optionen und bezahlt dafür mit mehr Speicher und langsamerem Indexaufbau.
- IVFFlat gruppiert Vektoren in Cluster um Zentroide und sucht dann nur in den der Query nächstgelegenen Clustern. Es baut schneller auf und braucht weniger Speicher als HNSW, benötigt aber repräsentative Daten, die vor der Erstellung bereits geladen sind. Auf einer leeren Tabelle erstellt, liefert es schlechte Cluster und schlechten Recall.
In Postgres mit der pgvector-Erweiterung ist jedes davon eine einzige Anweisung:
| |
Beide tauschen Exaktheit gegen Geschwindigkeit. Sie können den wirklich nächsten Vektor verfehlen – für Suche und Empfehlungen ist das in Ordnung, für einen Fall, der jedes Mal die exakte Antwort braucht, nicht.
Häufige Fehler bei der Suche mit Embeddings
- Mit einem Modell abfragen und mit einem anderen speichern. Vektoren aus zwei verschiedenen Modellen leben in inkompatiblen Räumen. Wenn Sie das Modell wechseln, berechnen Sie jedes gespeicherte Dokument mit dem neuen Modell neu. Eine Mischung aus alten und neuen Vektoren liefert nutzlose Treffer, ohne irgendeinen Fehler zu melden.
- Dot-Product-Ranking auf unnormalisierten Vektoren. Dot Product schließt die Länge mit ein, sodass ein langer Vektor einen kürzeren, relevanteren im Ranking überholen kann. Normalisieren Sie beim Schreiben, oder verwenden Sie die Cosine-Operatorklasse, die die Länge ignoriert.
- Nähe mit Korrektheit verwechseln. Ein hoher Cosine-Wert bedeutet “taucht in ähnlichen Kontexten auf”, nicht “ist die richtige Antwort”. Eine Anfrage zu den Nebenwirkungen eines Medikaments kann einen Abschnitt über die Dosierung desselben Medikaments zurückgeben: verwandt, aber falsch. Das ist der Hauptfehlermodus in RAG-Pipelines. Ähnlichkeit grenzt die Kandidaten ein. Sie überprüft sie nicht.
- Einen ANN-Index hinzufügen, bevor Brute Force wirklich zu langsam ist. Der Benchmark oben verarbeitet eine Million Vektoren in unter 100 ms. HNSW oder IVFFlat bringen Build-Zeit, zu justierende Parameter und ungenaue Ergebnisse mit sich. Es lohnt sich jenseits von Dutzenden Millionen Vektoren oder unter starker gleichzeitiger Last – nicht standardmäßig.
- Vergessen, neu zu embedden, wenn sich die Quelle ändert. Ein Embedding ist eine Momentaufnahme des Texts zum Zeitpunkt der Berechnung. Bearbeiten Sie das Dokument, ohne neu zu embedden, und die Suche liefert weiterhin die alte Version, mit vollem Vertrauen.
Brauchen Sie eine Vektordatenbank?
Zum Starten nicht. Berechnen Sie das Embedding einiger Hundert Ihrer echten Dokumente mit sentence-transformers, behalten Sie die Vektoren in einem NumPy-Array und durchsuchen Sie sie mit der Brute-Force-Schleife von oben. Keine Datenbank, kein Index. Wechseln Sie zu pgvector oder einer verwalteten Vektordatenbank, sobald Brute Force für Ihre Datenmenge und Ihr Abfragevolumen messbar zu langsam wird. Fügen Sie einen ANN-Index erst hinzu, wenn der Tabellenscan der Flaschenhals ist, den Sie tatsächlich gemessen haben.