Generating Embeddings
1. Choosing Embedding Models
| Model | Dim | Strength |
|---|---|---|
| OpenAI text-embedding-3-small | 1536 | Best cost/quality |
| OpenAI text-embedding-3-large | 3072 | Highest quality |
| Cohere embed-v3 | 1024 | Multilingual |
| Pinecone multilingual-e5-large | 1024 | Inference API, 100+ langs |
| SentenceTransformers MiniLM | 384 | Free, runs locally |
| Voyage voyage-3 | 1024 | Strong retrieval |
2. Using Sentence Transformers
Example: Local Embeddings
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("all-MiniLM-L6-v2")
embeddings = model.encode(["hello world", "foo bar"]).tolist()
3. Normalizing Embeddings
Example: L2 Normalize
import numpy as np
arr = np.array(embeddings)
normed = arr / np.linalg.norm(arr, axis=1, keepdims=True)
normed_list = normed.tolist()
| Reason | Benefit |
|---|---|
| Cosine ≡ dotproduct on unit | Faster, equivalent |
| Stable scoring | Removes magnitude bias |
4. Batch Processing Embeddings
Example: Batched OpenAI
from openai import OpenAI
client = OpenAI()
def embed_batch(texts, batch=100):
out = []
for i in range(0, len(texts), batch):
chunk = texts[i:i+batch]
r = client.embeddings.create(model="text-embedding-3-small", input=chunk)
out += [d.embedding for d in r.data]
return out
5. Caching Embeddings
Example: Hash-Keyed Cache
import hashlib, json, redis
r = redis.Redis()
def cached_embed(text, model="text-embedding-3-small"):
key = f"emb:{model}:{hashlib.sha256(text.encode()).hexdigest()}"
if (v := r.get(key)): return json.loads(v)
e = embed_one(text)
r.set(key, json.dumps(e), ex=86400 * 30)
return e
6. Validating Embedding Quality
| Test | Method |
|---|---|
| Sanity pairs | Similar texts should score > 0.7 |
| MTEB benchmark | Run on representative set |
| Retrieval@K | Measure recall on labeled set |
7. Reducing Embedding Dimensions
Example: Matryoshka Truncation
# OpenAI text-embedding-3 supports dimension truncation
r = client.embeddings.create(
model="text-embedding-3-large",
input="...",
dimensions=512, # truncate from 3072
)
| Method | Tradeoff |
|---|---|
| Matryoshka truncation | Native support; small accuracy loss |
| PCA | Need training data; larger loss |
| Quantization (int8) | ~4× storage savings |
8. Implementing Custom Embeddings
Example: HuggingFace
from transformers import AutoTokenizer, AutoModel
import torch
tok = AutoTokenizer.from_pretrained("BAAI/bge-large-en-v1.5")
mdl = AutoModel.from_pretrained("BAAI/bge-large-en-v1.5").eval()
def embed(text):
enc = tok(text, return_tensors="pt", truncation=True)
with torch.no_grad():
out = mdl(**enc)
return out.last_hidden_state[:, 0].squeeze().tolist()
9. Handling Embedding Drift
| Scenario | Action |
|---|---|
| Model version change | Re-embed all + reindex (cannot mix) |
| Versioning | Tag metadata embedding_model |
| A/B test | Use separate namespaces per model |
10. Optimizing Embedding Latency
| Tip | Effect |
|---|---|
| Batch calls | Amortize HTTP overhead |
| Co-locate with index | Same region/cloud |
| Cache by text hash | Skip duplicate work |
| GPU for local models | 10–100× speedup |
11. Managing Embedding Costs
| Lever | Effect |
|---|---|
| Smaller model | 3-small vs 3-large = ~6× cheaper |
| Truncate dims | Storage + query cost down |
| Cache repeated text | Avoid re-embedding |
| Batch API | OpenAI batch API 50% off |