What Are Text Embeddings?

Text embeddings convert text into numerical vectors that capture semantic meaning. Similar texts have similar vectors, enabling:

๐Ÿ’ก 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

ModelDimsMTEB ScoreSizeSpeed
OpenAI text-embedding-3-large307264.6APIFast
OpenAI text-embedding-3-small153662.3APIFast
BGE-M3102466.32.2GBMedium
BGE-large-en-v1.5102464.21.3GBMedium
E5-large-v2102462.01.3GBMedium
E5-mistral-7b-instruct409666.614GBSlow
all-MiniLM-L6-v238456.390MBVery Fast

Quick Recommendations

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

ProviderCost 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

By Resource Constraints

Production Tips

  1. Normalize embeddings: Always normalize for cosine similarity
  2. Batch processing: Process 32-128 texts at once
  3. Use FP16: Half precision is usually sufficient
  4. Cache embeddings: Store computed embeddings, don't recompute
  5. 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.