What Are Text Embeddings?
Text embeddings convert text into numerical vectors that capture semantic meaning. Similar texts have similar vectors, enabling:
- Semantic search: Find relevant documents by meaning, not keywords
- RAG systems: Retrieve context for LLM generation
- Clustering: Group similar documents
- Classification: Categorize text with few examples
๐ก What you'll learn
How to choose the right embedding model for your use case, deploy them on GPU for maximum throughput, and optimize costs.
Model Comparison
| Model | Dims | MTEB Score | Size | Speed |
|---|---|---|---|---|
| OpenAI text-embedding-3-large | 3072 | 64.6 | API | Fast |
| OpenAI text-embedding-3-small | 1536 | 62.3 | API | Fast |
| BGE-M3 | 1024 | 66.3 | 2.2GB | Medium |
| BGE-large-en-v1.5 | 1024 | 64.2 | 1.3GB | Medium |
| E5-large-v2 | 1024 | 62.0 | 1.3GB | Medium |
| E5-mistral-7b-instruct | 4096 | 66.6 | 14GB | Slow |
| all-MiniLM-L6-v2 | 384 | 56.3 | 90MB | Very Fast |
Quick Recommendations
- Best overall: BGE-M3 (multilingual, high quality)
- English only: BGE-large-en-v1.5
- Maximum speed: all-MiniLM-L6-v2
- Maximum quality: E5-mistral-7b-instruct
Using BGE Models
from sentence_transformers import SentenceTransformer
import torch
# Load BGE-M3 (best multilingual)
model = SentenceTransformer('BAAI/bge-m3')
# Or BGE-large for English
model = SentenceTransformer('BAAI/bge-large-en-v1.5')
# Move to GPU
model = model.to('cuda')
# Encode texts
texts = [
"Machine learning is a subset of artificial intelligence",
"Deep learning uses neural networks with many layers",
"The weather today is sunny and warm",
]
# BGE models work best with instruction prefix for queries
query_prefix = "Represent this sentence for searching relevant passages: "
queries = [query_prefix + "What is machine learning?"]
# Encode
doc_embeddings = model.encode(texts, normalize_embeddings=True)
query_embeddings = model.encode(queries, normalize_embeddings=True)
# Compute similarity
similarities = query_embeddings @ doc_embeddings.T
print(similarities) # [[0.82, 0.71, 0.12]]
Using E5 Models
from sentence_transformers import SentenceTransformer
# E5 models use different prefixes
model = SentenceTransformer('intfloat/e5-large-v2')
# For queries
queries = ["query: What is machine learning?"]
# For documents
documents = [
"passage: Machine learning is a subset of AI",
"passage: Deep learning uses neural networks",
]
query_emb = model.encode(queries, normalize_embeddings=True)
doc_emb = model.encode(documents, normalize_embeddings=True)
similarities = query_emb @ doc_emb.T
High-Throughput Embedding Service
from fastapi import FastAPI
from pydantic import BaseModel
from sentence_transformers import SentenceTransformer
import torch
from typing import List
app = FastAPI()
# Load model on startup
model = None
@app.on_event("startup")
async def load_model():
global model
model = SentenceTransformer('BAAI/bge-large-en-v1.5')
model = model.to('cuda')
model.eval()
# Warm up
model.encode(["warmup"], convert_to_tensor=True)
class EmbedRequest(BaseModel):
texts: List[str]
normalize: bool = True
class EmbedResponse(BaseModel):
embeddings: List[List[float]]
dimensions: int
model: str
@app.post("/embed", response_model=EmbedResponse)
async def embed(request: EmbedRequest):
with torch.no_grad():
embeddings = model.encode(
request.texts,
batch_size=32,
normalize_embeddings=request.normalize,
convert_to_numpy=True
)
return EmbedResponse(
embeddings=embeddings.tolist(),
dimensions=embeddings.shape[1],
model="bge-large-en-v1.5"
)
@app.get("/health")
async def health():
return {"status": "healthy", "model": "bge-large-en-v1.5"}
Batch Processing for Scale
import torch
from sentence_transformers import SentenceTransformer
from tqdm import tqdm
import numpy as np
def embed_large_dataset(texts, model, batch_size=64):
"""Embed millions of texts efficiently"""
model.eval()
all_embeddings = []
with torch.no_grad():
for i in tqdm(range(0, len(texts), batch_size)):
batch = texts[i:i + batch_size]
embeddings = model.encode(
batch,
normalize_embeddings=True,
convert_to_numpy=True,
show_progress_bar=False
)
all_embeddings.append(embeddings)
return np.vstack(all_embeddings)
# Usage
model = SentenceTransformer('BAAI/bge-large-en-v1.5').to('cuda')
texts = ["text " + str(i) for i in range(1_000_000)]
embeddings = embed_large_dataset(texts, model, batch_size=128)
print(f"Embedded {len(texts)} texts, shape: {embeddings.shape}")
Cost Comparison
| Provider | Cost per 1M tokens | ~Cost per 1M docs |
|---|---|---|
| OpenAI text-embedding-3-small | $0.02 | $4.00 |
| OpenAI text-embedding-3-large | $0.13 | $26.00 |
| Cohere embed-english-v3 | $0.10 | $20.00 |
| Self-hosted (GPUBrazil) | ~$0.001 | ~$0.20 |
Self-hosting is 20-130x cheaper at scale!
Embed Millions of Documents Affordably
Run BGE-M3 on GPUBrazil for pennies per million embeddings.
Get $5 Free Credit โChoosing the Right Model
By Use Case
- RAG/Search: BGE-large-en-v1.5 or BGE-M3
- Multilingual: BGE-M3 (100+ languages)
- Real-time: all-MiniLM-L6-v2 (fastest)
- Maximum quality: E5-mistral-7b-instruct
- Code search: CodeBERT or StarCoder embeddings
By Resource Constraints
- CPU only: all-MiniLM-L6-v2
- 4GB VRAM: BGE-large-en-v1.5
- 8GB+ VRAM: BGE-M3 or E5-large
- 24GB+ VRAM: E5-mistral-7b-instruct
Production Tips
- Normalize embeddings: Always normalize for cosine similarity
- Batch processing: Process 32-128 texts at once
- Use FP16: Half precision is usually sufficient
- Cache embeddings: Store computed embeddings, don't recompute
- Match prefixes: Use correct query/passage prefixes for each model
Conclusion
For most use cases, BGE-M3 or BGE-large-en-v1.5 offer the best balance of quality and speed. Run them on GPUBrazil to embed millions of documents at a fraction of API costs.