Embeddings konvertieren Text in hochdimensionale Vektoren, die Bedeutung repräsentieren. Ähnliche Texte haben ähnliche Vektoren.
Text: "Ein brauner Hund sitzt"
Embedding: [0.12, -0.45, 0.89, ..., -0.23] (384 oder 768 Dimensionen)
Text: "Der Hund ist braun"
Embedding: [0.15, -0.43, 0.87, ..., -0.21] (Sehr ähnlich!)
Cosine Similarity = 0.99 (sehr ähnlich)
Wie Embeddings trainiert werden
Contrastive Learning
Das Standard-Training-Paradigma.
Gegeben: Tripel (Anker, Positiv, Negativ)
- Anker: "Ein Hund sitzt"
- Positiv: "Der Hund sitzt" (paraphrase, ähnlich)
- Negativ: "Ein Apfel liegt" (nicht verwandt)
Ziel: Anker nahe bei Positiv, weit weg von Negativ
Triplet Loss
def triplet_loss(anchor, positive, negative, margin=0.5):
"""
anchor, positive, negative: embeddings (batch_size, embedding_dim)
"""
# Distanzen
pos_distance = torch.norm(anchor - positive, dim=1)
neg_distance = torch.norm(anchor - negative, dim=1)
# Loss: positive sollte näher als negative sein
loss = torch.relu(pos_distance - neg_distance + margin)
return loss.mean()
# Bedeutung:
# pos_distance < neg_distance - margin
# Wenn erfüllt: loss = 0 (perfect)
# margin = Sicherheitsabstand
Contrastive Loss (pairweise)
def contrastive_loss(embeddings_1, embeddings_2, labels, temperature=0.07):
"""
labels: 1 wenn Paar ähnlich, 0 wenn anders
"""
# Cosine Similarity
sim_matrix = torch.nn.functional.cosine_similarity(
embeddings_1.unsqueeze(1),
embeddings_2.unsqueeze(0),
dim=2
)
# Skalierung (temperature)
sim_matrix = sim_matrix / temperature
# Logistische Regression
loss = torch.nn.functional.binary_cross_entropy_with_logits(
sim_matrix,
labels.float()
)
return loss
MTEB Benchmark
Massive Text Embedding Benchmark - Standard Evaluation für Embeddings.
Aufgaben-Kategorien
1. Retrieval (30 Datasets)
- Finde relevante Dokumente für Queries
- Metrik: nDCG@10
2. Clustering (11 Datasets)
- Klustere ähnliche Texte
- Metrik: V-Measure
3. Semantic Search (14 Datasets)
- Finde semantisch ähnliche Texte
- Metrik: nDCG@10
4. Reranking (4 Datasets)
- Re-ranke Kandidaten nach Relevanz
- Metrik: nDCG@10
5. Pair Classification (3 Datasets)
- Bestimme ob Paar semantisch ähnlich
- Metrik: Accuracy
6. Classification (12 Datasets)
- Klassifiziere Texte
- Metrik: Accuracy
7. STS (Semantic Textual Similarity) (14 Datasets)
- Bewerte Ähnlichkeit von Paaren (0-5)
- Metrik: Spearman Correlation
Top Modelle (2024)
| Modell | Größe | Retrieval | STS | Clustering | Allgemein | Kosten |
|---|---|---|---|---|---|---|
| BGE-M3 | 568M | 64.6 | 86.2 | 46.8 | 73.5 | Free |
| E5-large-v2 | 335M | 63.2 | 87.1 | 44.2 | 72.4 | Free |
| Nomic-Embed | 137M | 61.7 | 85.3 | 43.1 | 69.8 | Free |
| OpenAI ada | 1.2B | 62.3 | 84.9 | 44.5 | 70.2 | $0.00002/1K |
| Cohere-V3 | Proprietary | 66.2 | 88.5 | 48.1 | 75.1 | $0.025/1M |
Popular Embedding Modelle
Sentence-Transformers (SBERT)
Basis der meisten Open-Source Embeddings.
from sentence_transformers import SentenceTransformer, util
# Modell laden
model = SentenceTransformer('all-MiniLM-L6-v2') # 22M params, 384-dim
# Embeddings generieren
sentences = [
"Ein Hund sitzt auf der Matte",
"Der Hund liegt auf dem Teppich",
"Ein Auto fährt auf der Straße"
]
embeddings = model.encode(sentences)
# Shape: (3, 384)
# Ähnlichkeit berechnen
similarity_matrix = util.pytorch_cos_sim(embeddings, embeddings)
print(similarity_matrix)
# [[1.00, 0.87, 0.12],
# [0.87, 1.00, 0.15],
# [0.12, 0.15, 1.00]]
OpenAI Embeddings (ada-002)
from openai import OpenAI
client = OpenAI()
response = client.embeddings.create(
input="Der schnelle braune Fuchs",
model="text-embedding-3-small" # 1536-dim, schnell
)
embedding = response.data[0].embedding
# [0.012, -0.045, ..., 0.089] (1536 dimensions)
BGE (BAAI General Embedding)
Best für Retrieval Tasks.
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("BAAI/bge-large-en-v1.5")
# 335M params, 768-dim
# Multi-lingual Version
model = SentenceTransformer("BAAI/bge-m3")
# Unterstützt 100+ Sprachen!
embeddings = model.encode(["Hallo", "Hello", "你好"])
# Alle drei sind semantisch ähnlich → ähnliche Embeddings
Nomic Embed
Effiziente Embeddings (kleineres Modell).
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("nomic-ai/nomic-embed-text-v1.5")
# 137M params, 768-dim
# Schneller als größere Modelle
# Trainiert auf großem Datenset (Nomic Corpus)
embeddings = model.encode(["text1", "text2"])
E5 (Entence Embeddings from Transformers)
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("intfloat/e5-large-v2")
# Wichtig: E5 erwartet prefix!
sentences = [
"passage: Ein Hund sitzt auf einer Matte",
"query: Wo sitzt der Hund?"
]
embeddings = model.encode(sentences, prompt_name="")
# Prefix ändert Embedding Space!
Embedding Space Dimensionen
Größere Dimensionen:
OpenAI ada: 1536-dim → Mehr Nuancen, aber größer
BGE/E5: 768-dim → Balanciert
MiniLM: 384-dim → Schnell, kleiner
Regel:
Höhere Dimensionalität → Bessere Qualität
Aber: 768-dim ist meist Sweetspot (Qualität + Speed)
Semantische Suche (praktisch)
Vector Database Setup
from sentence_transformers import SentenceTransformer
import faiss
import numpy as np
# 1. Modell laden
model = SentenceTransformer("all-MiniLM-L6-v2")
# 2. Dokumente einbetten
documents = [
"Python ist eine Programmiersprache",
"Java wird oft für Backend verwendet",
"JavaScript läuft im Browser",
"Machine Learning ist ein Gebiet der KI"
]
doc_embeddings = model.encode(documents, convert_to_numpy=True)
# Shape: (4, 384)
# 3. FAISS Index erstellen (Ähnlichkeitssuche)
index = faiss.IndexFlatL2(384) # L2 Distance
index.add(doc_embeddings.astype('float32'))
# 4. Query
query = "Was ist eine Programmiersprache?"
query_embedding = model.encode(query, convert_to_numpy=True)
distances, indices = index.search(
query_embedding.astype('float32').reshape(1, -1),
k=2 # Top 2 Ergebnisse
)
print("Top Results:")
for idx in indices[0]:
print(f" - {documents[idx]}")
Mit Vector DB (Weaviate, Milvus, Pinecone)
import weaviate
# Weaviate Client
client = weaviate.Client("http://localhost:8080")
# Schema
client.schema.create_class({
"class": "Document",
"vectorizer": "text2vec-transformers",
"properties": [
{"name": "text", "dataType": ["string"]},
{"name": "source", "dataType": ["string"]}
]
})
# Indexieren
documents = [
{"text": "Python ist great für ML", "source": "blog1"},
{"text": "JavaScript für Web Development", "source": "blog2"}
]
for doc in documents:
client.data_object.create(
class_name="Document",
data_object=doc,
vector=model.encode(doc["text"]) # Embedding
)
# Suchen
query = "ML Sprachen"
result = client.query.get(
"Document",
["text", "source"]
).with_near_text({
"concepts": [query]
}).with_limit(3).do()
print(result)
Finetuning von Embeddings
Für Task-spezifische Verbesserungen.
from sentence_transformers import SentenceTransformer, InputExample, losses
from torch.utils.data import DataLoader
# Modell laden
model = SentenceTransformer("all-MiniLM-L6-v2")
# Training Data
train_examples = [
InputExample(
texts=["Hund sitzt", "Der Hund sitzt"],
label=0.9 # 0-1, wie ähnlich
),
InputExample(
texts=["Hund sitzt", "Auto fährt"],
label=0.1 # Nicht ähnlich
),
# ... mehr Beispiele
]
# DataLoader
train_dataloader = DataLoader(train_examples, shuffle=True, batch_size=16)
# Loss Function
train_loss = losses.CosineSimilarityLoss(model)
# Finetuning
model.fit(
train_objectives=[(train_dataloader, train_loss)],
epochs=10,
warmup_steps=100,
show_progress_bar=True
)
model.save_pretrained("./my-embeddings")
Best Practices
1. Model Wahl basierend auf Use Case
Retrieval (Dokumente suchen):
→ BGE oder E5 (spezialisiert)
Clustering:
→ Größere Modelle (768-dim+)
Speed kritisch:
→ Nomic oder MiniLM
Hohe Qualität:
→ BGE-M3 oder OpenAI ada
2. Batch Processing für große Datenmengen
# ❌ Langsam: Ein Document nach dem anderen
for doc in documents:
embedding = model.encode(doc)
# ✅ Schnell: Batch Processing
embeddings = model.encode(
documents,
batch_size=128,
show_progress_bar=True,
convert_to_numpy=True
)
3. Normalisierung und Skalierung
from sklearn.preprocessing import normalize
# Normalisiere auf Unit Sphere (für Cosine Similarity)
embeddings_normalized = normalize(embeddings, norm='l2')
# Cosine Similarity ist dann einfach Dot Product
similarity = embeddings_normalized @ embeddings_normalized.T
Häufige Fehler
1. Falsche Prefix (bei E5)
❌ Ohne Prefix
text = "Python ist great"
embedding = model.encode(text)
✅ Mit Prefix
text = "passage: Python ist great" # oder "query: ..."
embedding = model.encode(text)
2. Zu kleine Dimensionen
❌ 256-dim: Zu viel Informationsverlust
✅ 384-dim+: Guter Balance
3. Keine Normalisierung bei Cosine Similarity
❌ Cosine Similarity ohne Normalisierung
sim = dot_product(e1, e2)
✅ Mit Normalisierung
e1_norm = e1 / ||e1||
e2_norm = e2 / ||e2||
sim = dot_product(e1_norm, e2_norm)
