Die semantische Suche ist ein wichtiger Bestandteil in LLM-gestützten Anwendung. Durch Anwendung des RAG-Patterns (Retrieval Augmented Generation) können wir dank semantischer Suche, die Antworten eines LLMs durch eigene Inhalte anreichern. Im Herzen der semantischen Suche liegt der Vergleich von Vektoren. Der Artikel untersucht verschiedene Methoden und zeigt deren Grenzen.
Vektoren und Embedding Modelle
Embedding Modelle berechnen für semantisch ähnliche Textstelle benachbarte Vektoren. Warum das so ist, ergibt sich aus der Art und Weise, wie die Embedding Modelle erstellt werden.
Dass es so gut funktioniert, erstaunt noch immer auch die Experten.
Es gibt verschiedene Embedding Modelle. Wir untersuchen in diesem Artikel Word Embeddings. Denkbar ist auch der Einsatz von Sentence Embeddings, Paragraph-Embeddings – je nachdem, wie das Modell trainiert wurde. Das Tutorial Sentence Embeddings für Vektordatenbanken zeigt, wie ein Index für eine Vektordatenbank aufgebaut werden kann.
Das Konzept der Suche ist für jede Art Embedding identisch. Wir widmen uns Word-Embeddings.
Das Bild verdeutlicht den Ansatz – vereinfacht im zweidimensionalen Raum: Der Vektor für das Konzept ‘Prince’ liegt näher an den Vektoren für die Konzepte ‘King’ und ‘Queen’ als an Vektoren für die Konzepten ‘Man’ und ‘Woman’. Das ist eine fundamentale Eigenschaft der Word-Embeddings.
Wir nutzen diese Eigenschaft bei der semantischen Suche. Bei der Keyword-Suche helfen Vektoren wenig.
Keyword-Suche kennen wir beispielsweise von Google: Wir tippen Schlüsselwörter ein und erhalten als Suchergebnisse Dokumente gezeigt, in denen die Schlüsselwörter auftauchen.
Semantische Suche funktioniert anders: Hier tippen wir einen ganzen Satz ein oder sogar mehrere Sätze. Das Ergebnis sind Textfragmente oder auch ganze Dokumente, die inhaltlich zu unseren Suchsätzen passen. Und hier glänzt die Vektorsuche.
- Zur Inexierungszeit werden Textfragmente in Vektoren umgewandelt. Die Wahl der Grösse eines Textfragments ist eine Designentscheidung.
- Zur Suchzeit wird unsere Suchfrage zu Vektoren umgewandelt.
- Die Treffer der semantischen Suche entsprechen denjenigen Textfragmenten, deren Vektoren nahe an den Vektoren der Suchfrage liegen. Sie weisen einen Inhaltlichen Zusammenhang mit der Suchfrage auf ohne, dass die Wörter aus der Suchfrage mit denjenigen aus dem Textfragment übereinstimmen.
Was bedeutet ‘nahe’ in diesem Zusammenhang?
Hier kommen Metriken, also Abstandsfunktionen, zum Zuge.
In der Mathematik ist eine Metrik eine Funktion, die den Abstand zwischen zwei Punkten misst.
Formale Definition einer Metrik (Abstandsfunktion)
Eine Metrik (auch Abstandsfunktion) auf einer Menge M ist eine Funktion d: M x M → ℝ, die die folgenden Eigenschaften erfüllt:
- Nichtnegativität: Für alle x, y ∈ M gilt: d(x, y) ≥ 0
- Identität: Für alle x ∈ M gilt: d(x, x) = 0
- Symmetrie: Für alle x, y ∈ M gilt: d(x, y) = d(y, x)
- Dreiecksungleichung: Für alle x, y, z ∈ M gilt: d(x, z) ≤ d(x, y) + d(y, z)
Erläuterung:
- Nichtnegativität: Der Abstand zwischen zwei Punkten kann niemals negativ sein.
- Identität: Der Abstand eines Punktes zu sich selbst ist immer 0.
- Symmetrie: Der Abstand zwischen zwei Punkten ist gleich dem Abstand in umgekehrter Richtung.
- Dreiecksungleichung: Intuitiv besagt dies, dass der kürzeste Weg zwischen zwei Punkten niemals länger sein kann als die Summe der Entfernungen über einen Zwischenpunkt.
Beispiele für Metriken in der Vektorsuche
Euklidische Metrik
Die euklidische Metrik fällt uns zuerst ein. Sie misst die Luftlinie zwischen zwei Punkten im Raum. Das Bild verdeutlicht die Metrik.
Das Codefragment berechnet die Euklidische Metrik (oder Norm) in Python:
import math v1 = [1, 2, 3] # Vektor 1 v2 = [4, 5, 6] # Vektor 2 quadratischer_abstand = sum((a - b)**2 for a, b in zip(v1, v2)) abstand = math.sqrt(quadratischer_abstand)
oder einfacher mit numpy
import numpy as np v1 = np.array([1, 2, 3]) # Vektor 1 als NumPy-Array v2 = np.array([4, 5, 6]) # Vektor 2 als NumPy-Array abstand = np.linalg.norm(v1 - v2) # Euklidische Distanz berechnen
Die Manhattan Distanz
Die Manhattan Distanz oder auch City-Block-Distanz erfüllt die Definition einer Metrik.
Sie misst den Abstand zwischen zwei Punkten in einem gitterförmigen Raum und zwar so ähnlich, wie wenn wir in den Strassenschluchen in Manhattan ausrechnen, wie viele Block-Längen wir laufen müssen, um von A nach B zu gelangen.
Das Bild verdeutlicht das Konzept: Wir ‘laufen’ den Achsen entlang – im zweidimensionalen Raum beachten wir die zwei Achsen, Word-Embeddings finden in hochdimensionalen Räumen statt und entsprechend viele Achsen sind dann zu berücksichtigen.
Hier der Python Code (ohne Ausnahmebehandlung)
v1 = [1, 2, 3] v2 = [4, 5, 6] abstand = 0 for i in range(len(v1)): abstand += abs(v1[i] - v2[i])
Wie auch die Euklidische Distanz erfüllt die Manhattan Distanz die Anforderungen der Definition einer Distanzmetrik.
Als nächsten untersuchen wir zwei sehr beliebte Funktionen, die in Vektordatenbanken gerne eingesetzt werden:
Cosinus-Ähnlichkeit (cosinus similarity)
Diese Ähnlichkeitsfunktion misst den Winkel zwischen zwei Vektoren. Das Bild zeigt die Heuristik: Je kleiner der Winkel, umso ähnlicher sind die Konzepte. Na ja, relativ ähnlich, so wie das Bild zeigt. Der Winkel zwischen den Vektoren die Prince und King sowie Prince und Queen zugeordnet sind, sind deutlich kleiner als die Winkel zwischen den Vektoren für das Prince-Woman respektive das Prince-Man-Paar.
Hier der Python Code:
Zuerst stellen wir sicher, dass wir nicht durch 0 dividieren werden.
Danach berechnen wir das Skalar-Produkt der beiden Vektoren (englisch: dot-Produkt) und dividieren durch die Länge, also Norm, der beiden Vektoren.
import numpy as np v1 = np.array([1, 2, 3]) # Vektor 1 als NumPy-Array v2 = np.array([4, 5, 6]) # Vektor 2 als NumPy-Array v1_norm = np.linalg.norm(v1) v2_norm = np.linalg.norm(v2) if v1_norm == 0 or v2_norm == 0: abstand = 0 else: abstand = np.dot(v1, v2) / (v1_norm * v2_norm)
Das Skalarprodukt können wir auch ohne numpy berechnen und zwar:
sum(a * b for a, b in zip(v1, v2))
Oder ganz ohne Python-Funktionen
v1 = [1, 2, 3] # Vektor 1 als Liste v2 = [4, 5, 6] # Vektor 2 als Liste abstand = 0 for i in range(len(v1)): abstand += v1[i] * v2[i]
Skalarprodukt
Auch das Skalarprodukt wird oft zur Vektorsuche verwendet. Ein größeres Skalarprodukt bedeutet, dass ein Vektor mehr auf den anderen projiziert, was auf mehr gemeinsame Informationen oder Merkmale hinweist.
Das Skalarprodukt der Einheitsvektoren entspricht dem Cosinus der beiden Vektoren. In dem Fall fällt in Bezug auf die Formel zur Cosinus-Ähnlichkeit die Division durch das Produkt der beiden Normen weg.
Zur Bildung der Norm wird ja der verallgemeinerte Satz von Pythagoras verwendet, was ziemlich rechenintensiv ist. Da geht es einfacher, nur das Skalarprodukt zu nehmen – dieses ist präzise genug und erst noch einfacher zu berechnen.
Die Cosinus-Ähnlichkeit ist keine Metrik
Schauen wir genau hin, dann erfüllen weder die Cosinusähnlichkeit noch das Skalarprodukt die Anforderungen an einer Metrik:
Die Cosinusähnlichkeit verletzt unter anderem die Identität: Eine Abstandsfunktion muss für alle Punktepaare d(x, y) > 0 sein, außer wenn x und y identisch sind (d(x, x) = 0). Die Cosinusähnlichkeit und auch das Skalarprodukt, kann jedoch auch für unterschiedliche Punkte 1 ergeben (z.B. wenn die Vektoren die gleiche Richtung haben).
Das Bild zeigt, was passieren kann:
Der Winkel zwischen ‘Whiteboard’ und ‘Price’ ist in diesem hypothetischen und stark vereinfachten Bild gleich Null.
Das bedeutet: Für die Vektorsuche stimmen die Konzepte ‘Whiteboard’ und ‘Prince’ semantisch überein. Das ist nicht im Sinne der semantischen Suche.
Für das Skalarprodukt liesse sich ein analoges Beispiel konstruieren.
Fazit
Für die semantische Suche in Vektordatenbanken ist die Ausführungsgeschwindigkeit wichtiger als mathematischen Präzision. Und deshalb lohnt es sich, verschiedene Metriken bei der Vektorsuche zu testen.
Welche Abstandsfunktion für unsere Anwendungsfälle die beste ist, werden wir empirisch herausfinden müssen.
Die Qualität der semantischen Suche ist nicht unerheblich, wenn es um RAG, also Retrieval Augmented Generation geht – denn seit es generative KI gibt, ist der Bedarf nach semantischen Suchsystemen erheblich gestiegen.