Zum Hauptinhalt springen
Lektion 5 von 22 · Embeddings, RAG und semantische Suche

Embeddings visualisieren

Lektion 5 von 22 Aktualisiert am 29. Juli 2026 Entwickler
Auf dieser Seite
  1. Lernziele
  2. Die Herausforderung: 3.072 Dimensionen
  3. Dimensionsreduktion mit t-SNE
  4. Reduktion mit UMAP
  5. Interaktive 3D-Visualisierung
  6. Visualisierung der Wirkung der Dimensionsreduktion
  7. Zusammenfassung

Lernziele

  • Hochdimensionale Vektoren für die 2D/3D-Visualisierung reduzieren
  • t-SNE und UMAP zur Visualisierung einsetzen
  • Visuelle Cluster interpretieren

Die Herausforderung: 3.072 Dimensionen

Embeddings leben in einem 3.072-dimensionalen Raum, den niemand direkt visualisieren kann. Um sie zu untersuchen, müssen Sie sie in zwei oder drei Dimensionen projizieren. Dies akzeptiert einen gewissen Informationsverlust, bewahrt aber die wesentliche Struktur: Punkte, die nah beieinander lagen, sollten es auch nach der Projektion bleiben. Dies ist ein leistungsfähiges Diagnoseinstrument. Ein getrennter Cluster oder überlappende Themen fallen auf einen Blick auf, während aggregierte Metriken diese möglicherweise völlig übersehen.

Dimensionsreduktion mit t-SNE

t-SNE (t-distributed Stochastic Neighbor Embedding) ist die klassische Methode. Es findet eine 2D-Anordnung, bei der jeder Punkt seine ursprüngliche Nachbarschaft beibehält. Das folgende Beispiel erstellt absichtlich einen Korpus mit drei eindeutigen Themen – KI, Kochen und Sport –, damit Sie überprüfen können, ob die Projektion die erwartete Struktur wiederherstellt.

from openai import OpenAI
from sklearn.manifold import TSNE
import matplotlib.pyplot as plt
import numpy as np

client = OpenAI()

# Beispielkorpus
textes = [
    # KI-Cluster
    "Deep learning uses neural networks",
    "Machine learning transforms data science",
    "Transformers revolutionized NLP",
    "GPT-5 is a large language model",
    # Kochen-Cluster
    "Chocolate cake recipe",
    "How to prepare ratatouille",
    "Basics of French pastry",
    "Oven baking techniques",
    # Sport-Cluster
    "Paris Marathon attracts thousands of runners",
    "Interval training for running",
    "Soccer rules explained",
    "Sports nutrition and performance",
]

categories = (["AI"] * 4 + ["Cooking"] * 4 + ["Sports"] * 4)

# Embeddings generieren
response = client.embeddings.create(
    input=textes,
    model="text-embedding-3-large"
)
embeddings = np.array([d.embedding for d in response.data])

# t-SNE-Reduktion auf 2D
tsne = TSNE(n_components=2, random_state=42, perplexity=4)
coords = tsne.fit_transform(embeddings)

# Visualisierung
couleurs = {"AI": "#2563eb", "Cooking": "#dc2626", "Sports": "#16a34a"}
fig, ax = plt.subplots(figsize=(10, 8))

for cat in couleurs:
    mask = [c == cat for c in categories]
    ax.scatter(
        coords[mask, 0], coords[mask, 1],
        c=couleurs[cat], label=cat, s=100, alpha=0.8
    )
    for i, (x, y) in enumerate(coords[mask]):
        idx = [j for j, m in enumerate(mask) if m][i]
        ax.annotate(
            textes[idx][:30] + "...",
            (x, y), fontsize=7, alpha=0.7
        )

ax.legend()
ax.set_title("t-SNE Visualization of Embeddings")
plt.tight_layout()
plt.savefig("tsne_embeddings.png", dpi=150)
plt.show()

Drei Parameter steuern das Ergebnis. perplexity balanciert die lokale gegenüber der globalen Struktur, wobei typische Werte zwischen 5 und 50 liegen; hier ist es auf 4 eingestellt, da der Korpus nur zwölf Punkte enthält und ein Perplexity-Wert höher als die verfügbaren Nachbarn eine inkohärente Projektion erzeugen würde. n_iter legt die Anzahl der Optimierungsiterationen fest, wobei ein Minimum von 250 und idealerweise mehr als 1.000 erforderlich ist. random_state gewährleistet Reproduzierbarkeit: Ohne diesen Parameter liefern zwei aufeinanderfolgende Ausführungen unterschiedliche Diagramme, was Vergleiche unmöglich macht.

Reduktion mit UMAP

UMAP (Uniform Manifold Approximation and Projection) wird oft gegenüber t-SNE bevorzugt, da es schneller ist und die globale Struktur besser bewahrt. Mit anderen Worten: Der Abstand zwischen zwei Clustern in einem UMAP-Diagramm bedeutet etwas, während er bei t-SNE willkürlich ist.

import umap

# UMAP-Reduktion auf 2D
reducer = umap.UMAP(n_components=2, random_state=42, n_neighbors=5)
coords_umap = reducer.fit_transform(embeddings)

fig, ax = plt.subplots(figsize=(10, 8))

for cat in couleurs:
    mask = [c == cat for c in categories]
    ax.scatter(
        coords_umap[mask, 0], coords_umap[mask, 1],
        c=couleurs[cat], label=cat, s=100, alpha=0.8
    )

ax.legend()
ax.set_title("UMAP Visualization of Embeddings")
plt.tight_layout()
plt.savefig("umap_embeddings.png", dpi=150)
plt.show()

Die folgende Tabelle fasst die Abwägungen zwischen den beiden Methoden zusammen.

Kriteriumt-SNEUMAP
GeschwindigkeitLangsam (O(n²))Schnell
Globale StrukturSchlecht erhaltenGut erhalten
ReproduzierbarkeitVariabelStabil
Parameterperplexityn_neighbors, min_dist

Interaktive 3D-Visualisierung

Bei einem Korpus mit einigen hundert Punkten wird ein statisches Diagramm unlesbar: Beschriftungen überlappen, und man kann nicht erkennen, welchem Text welcher Punkt entspricht. Plotly löst dies, indem es Informationen in Tooltips verschiebt und die Rotation der Punktwolke ermöglicht.

import plotly.express as px
import pandas as pd

# Reduktion auf 3D
reducer_3d = umap.UMAP(n_components=3, random_state=42, n_neighbors=5)
coords_3d = reducer_3d.fit_transform(embeddings)

df = pd.DataFrame({
    "x": coords_3d[:, 0],
    "y": coords_3d[:, 1],
    "z": coords_3d[:, 2],
    "category": categories,
    "text": [t[:50] for t in textes]
})

fig = px.scatter_3d(
    df, x="x", y="y", z="z",
    color="category",
    hover_data=["text"],
    title="3D Embeddings"
)
fig.write_html("embeddings_3d.html")
fig.show()

Die resultierende HTML-Datei ist bereit zum Teilen. Dies ist eine hervorragende Möglichkeit, einem Geschäftsteam zu zeigen, wie ihre Dokumente verteilt sind, ohne eine einzige Zeile Code zu schreiben.

Visualisierung der Wirkung der Dimensionsreduktion

In Lektion 2 wurde festgestellt, dass das Trunkieren eines Vektors auf 256 Dimensionen etwa 88 % seiner Qualität bewahrt. Diese Zahl bleibt abstrakt, bis man sie sieht. Indem Sie denselben Korpus in vier verschiedenen Größen generieren und jede Version projizieren, beobachten Sie direkt, wann Cluster beginnen, sich zu berühren. Dies ermöglicht es Ihnen, basierend auf Beweisen statt einer allgemeinen Regel zu entscheiden.

dimensions_a_tester = [256, 512, 1536, 3072]

fig, axes = plt.subplots(1, 4, figsize=(24, 5))

for ax, dim in zip(axes, dimensions_a_tester):
    resp = client.embeddings.create(
        input=textes,
        model="text-embedding-3-large",
        dimensions=dim
    )
    embs = np.array([d.embedding for d in resp.data])
    tsne = TSNE(n_components=2, random_state=42, perplexity=4)
    coords = tsne.fit_transform(embs)

    for cat in couleurs:
        mask = [c == cat for c in categories]
        ax.scatter(coords[mask, 0], coords[mask, 1],
                   c=couleurs[cat], label=cat, s=60)
    ax.set_title(f"{dim} dimensions")
    ax.legend(fontsize=7)

plt.tight_layout()
plt.savefig("comparaison_dimensions.png", dpi=150)
plt.show()

Eine Warnung vor der Interpretation dieser Diagramme: Projektionen verzerren immer. Ein isolierter Punkt im Diagramm ist nicht unbedingt isoliert im ursprünglichen Raum, und zwei in 2D feststeckende Cluster können im 3.072-dimensionalen Raum perfekt getrennt sein. Visualisierung hilft Ihnen, eine Hypothese zu formulieren, niemals sie zu validieren. Die Validierung stützt sich auf die Metriken, die wir in Lektion 10 behandeln werden.

Zusammenfassung

  • t-SNE und UMAP projizieren Embeddings in 2D/3D zur Visualisierung
  • UMAP wird im Allgemeinen aufgrund seiner Geschwindigkeit und Treue bevorzugt
  • Plotly ermöglicht interaktive 3D-Visualisierungen
  • Visualisierung hilft bei der Validierung der Embedding-Qualität und der Erkennung von Anomalien