Semantische Ähnlichkeit
Auf dieser Seite
Messung der Textnähe
Sobald Sie Ihre Texte mithilfe von mistral-embed in Vektoren umgewandelt haben, können Sie quantifizieren, wie semantisch nah zwei Texte zueinander stehen. Dies ist das Kernstück der semantischen Suche: Es werden Bedeutungen verglichen, nicht nur Wörter. Während eine SQL-Abfrage wie LIKE '%invoice%' lediglich ein einfaches Ja oder Nein zurückgibt, liefert ein Ähnlichkeitsmetrik eine Zahl, die eine Rangfolge ermöglicht. Diese Rangfolge ist der Grund, warum das gesamte System wertvoll ist.
In dieser Lektion werden Sie die wichtigsten Ähnlichkeitsmetriken implementieren und deren Funktionsweise anhand konkreter Python-Beispiele kennenlernen.
Euklidische Distanz
Die euklidische Distanz misst die Länge des Segments zwischen zwei Punkten im Vektorraum. Je kleiner die Distanz, desto ähnlicher sind die Texte. Im folgenden Beispiel werden drei Sätze verglichen: Die ersten beiden sagen dasselbe mit unterschiedlichem Vokabular, während der dritte über Apfelkuchen spricht. Die Sätze A und B haben keine gemeinsamen Wörter, doch ihre Distanz wird deutlich geringer sein als die Distanz zwischen A und C.
import numpy as np
from mistralai.client import Mistral
client = Mistral(api_key=api_key)
def get_embedding(text):
"""Erstellt das Embedding für einen Text über mistral-embed."""
response = client.embeddings.create(
model="mistral-embed",
inputs=[text],
)
return np.array(response.data[0].embedding)
# Drei Sätze zum Vergleich
phrase_a = "Machine learning transforms data into predictions."
phrase_b = "Automatic learning converts data into results."
phrase_c = "The recipe for apple pie is simple."
emb_a = get_embedding(phrase_a)
emb_b = get_embedding(phrase_b)
emb_c = get_embedding(phrase_c)
# Euklidische Distanzen
dist_ab = np.linalg.norm(emb_a - emb_b)
dist_ac = np.linalg.norm(emb_a - emb_c)
print(f"Distanz A-B (semantisch nah): {dist_ab:.4f}")
print(f"Distanz A-C (semantisch entfernt): {dist_ac:.4f}")
# A-B wird deutlich niedriger sein als A-C
Kosinus-Ähnlichkeit
Die kosinus-ähnlichkeit ist das am weitesten verbreitete Maß für Embeddings. Sie misst den Winkel zwischen zwei Vektoren, unabhängig von deren Größe. Das Ergebnis reicht von -1 bis 1 und bietet eine sofort lesbare Skala: Sie können eine Schwelle festlegen und sie einem Geschäftsteam mitteilen, ohne Vektornormen erklären zu müssen.
from sklearn.metrics.pairwise import cosine_similarity
# Mit scikit-learn
sim_ab = cosine_similarity([emb_a], [emb_b])[0][0]
sim_ac = cosine_similarity([emb_a], [emb_c])[0][0]
print(f"Kosinus-Ähnlichkeit A-B: {sim_ab:.4f}") # ~0.92 (sehr nah)
print(f"Kosinus-Ähnlichkeit A-C: {sim_ac:.4f}") # ~0.35 (entfernt)
Die Lücke zwischen 0,92 und 0,35 ist die nützliche Information: Es ist nicht der absolute Wert, der zählt, sondern der Abstand, der in Ihrem spezifischen Datensatz relevante Ergebnisse von abseits-thematischen trennt. Sie können das Maß auch manuell implementieren, um zu überprüfen, was scikit-learn tut, oder um Abhängigkeiten in einer eingeschränkten Umgebung zu vermeiden:
def cosine_sim(vec_a, vec_b):
"""Manuelle Berechnung der kosinus-ähnlichkeit."""
dot = np.dot(vec_a, vec_b)
norm_a = np.linalg.norm(vec_a)
norm_b = np.linalg.norm(vec_b)
return dot / (norm_a * norm_b)
print(f"Manuelle Überprüfung A-B: {cosine_sim(emb_a, emb_b):.4f}")
Ähnlichkeitsmatrix
Um mehrere Dokumente gegeneinander zu vergleichen, können Sie eine Ähnlichkeitsmatrix erstellen. Der folgende Korpus mischt absichtlich zwei Themen – KI und Gastronomie –, sodass die Struktur sofort ins Auge springt: Dokumente 0, 1 und 3 stehen in Beziehung zueinander, während Dokumente 2 und 4 eine separate Cluster bilden. Genau dieses Signal nutzen Clustering-Algorithmen aus.
from sklearn.metrics.pairwise import cosine_similarity
documents = [
"Mistral AI develops language models.",
"LLMs generate text from prompts.",
"French cuisine is renowned worldwide.",
"NLP is a branch of artificial intelligence.",
"Croissants taste best in the morning.",
]
# Kodierung aller Dokumente
response = client.embeddings.create(
model="mistral-embed",
inputs=documents,
)
embeddings = np.array([d.embedding for d in response.data])
# Ähnlichkeitsmatrix (5x5)
sim_matrix = cosine_similarity(embeddings)
print("Ähnlichkeitsmatrix:")
for i, doc in enumerate(documents):
for j, doc2 in enumerate(documents):
if j > i:
print(f" [{i}]-[{j}]: {sim_matrix[i][j]:.3f}")
Einfache semantische Suche
All das oben Geschilderte kombiniert sich zu einer Mini-Suchmaschine. Die Anfrage wird mit demselben Modell wie der Korpus kodiert – eine nicht verhandelbare Bedingung, da zwei verschiedene Modelle unvergleichbare Räume erzeugen – und argsort kehrt die Rangfolge um, um die höchsten Scores an die Spitze zu bringen.
def recherche_semantique(query, documents, embeddings, top_k=3):
"""Findet die relevantesten Dokumente für eine Anfrage."""
# Kodierung der Anfrage
query_response = client.embeddings.create(
model="mistral-embed",
inputs=[query],
)
query_embedding = np.array(query_response.data[0].embedding)
# Berechnung der Ähnlichkeiten
similarities = cosine_similarity(
[query_embedding], embeddings
)[0]
# Sortierung nach absteigender Ähnlichkeit
ranked_indices = np.argsort(similarities)[::-1][:top_k]
results = []
for idx in ranked_indices:
results.append({
"document": documents[idx],
"score": similarities[idx],
"index": int(idx),
})
return results
# Beispiel für die Verwendung
query = "Wie funktioniert die KI von Mistral?"
results = recherche_semantique(query, documents, embeddings)
for r in results:
print(f" Score: {r['score']:.3f} -- {r['document']}")
Relevanzschwelle
Dieser Motor hat einen Mangel, den Sie in der Produktion schnell entdecken werden: Er gibt immer drei Ergebnisse zurück, selbst wenn die Frage nichts mit dem Korpus zu tun hat. Fragen Sie ihn nach dem Wetter, und er wird die drei am wenigsten entfernten Dokumente zurückgeben, mit lächerlichen Scores, aber einer scheinbar normalen Rangfolge. In der Praxis müssen Sie daher eine Ähnlichkeitsschwelle definieren, unterhalb derer ein Dokument als irrelevant gilt:
SEUIL_PERTINENCE = 0.7
def recherche_avec_seuil(query, documents, embeddings, seuil=SEUIL_PERTINENCE):
"""Suche mit Filterung nach Relevanzschwelle."""
results = recherche_semantique(query, documents, embeddings, top_k=len(documents))
return [r for r in results if r["score"] >= seuil]
# Eine Schwelle von 0,7 ist ein guter Ausgangspunkt
# Passen Sie sie basierend auf Ihrem Anwendungsfall an:
# - 0,8+: sehr streng (wenige Ergebnisse, hohe Relevanz)
# - 0,6-0,7: ausgewogen
# - 0,5: permissiv (viele Ergebnisse, variable Relevanz)
Nehmen Sie sich die Zeit, diese Schwelle auf etwa dreißig echten Anfragen zu kalibrieren, bevor Sie den Dienst für Benutzer öffnen. Stellen Sie sie zu hoch, und Ihr Motor antwortet legitimen Fragen mit „Ich weiß es nicht”. Stellen Sie sie zu niedrig, und Sie füttern das LLM mit abseits-thematischen Kontexten, wodurch genau die Halluzinationen entstehen, die RAG eigentlich verhindern sollte.
Kernaussagen
- Die kosinus-ähnlichkeit ist das Standardmaß zum Vergleich von Embeddings.
- Verwenden Sie
sklearn.metrics.pairwise.cosine_similarityfür schnelle Berechnungen. - Eine Ähnlichkeitsmatrix ermöglicht es, alle Dokumente gegeneinander zu vergleichen.
- Definieren Sie eine Relevanzschwelle, die an Ihren Anwendungsfall angepasst ist (0,7 ist eine gute Vorgabe).
- Die semantische Suche findet Ergebnisse nach Bedeutung, nicht nach exakter Wortübereinstimmung.