DS DevShelfHub Projects · AI tools
Tutorials / RAG & Vector DBs / Embeddings
RAG Pipeline Beginner · 14 min read Page 5 of 23

Embeddings & Vectors

By DevShelfHub

Choosing embedding models, understanding dimensions, comparing OpenAI vs open-source, batch processing, caching, and optimizing for cost and quality.

Series progress5 / 23
RAG Embeddings — RAG pipeline tutorial

Popular Embedding Models

You have three options: expensive but best (OpenAI), fast and free (open-source), or custom (fine-tuned).

1️⃣ OpenAI Embeddings (Closed-source, proprietary)

Models:

  • text-embedding-3-small — 1536 dims, $0.02 per 1M tokens ⭐ RECOMMENDED
  • text-embedding-3-large — 3072 dims, $0.13 per 1M tokens (overkill for most)

Pros: Best quality, multilingual, OpenAI keeps improving them, no setup.

Cons: Proprietary, cost per query, requires internet, data privacy concerns.

2️⃣ Open-Source Models (Free, run locally)

Popular models:

  • sentence-transformers/all-mpnet-base-v2 — 768 dims, excellent quality (~$0)
  • nomic-ai/nomic-embed-text-v1 — 768 dims, very competitive (~$0)
  • jinaai/jina-embeddings-v2-base-en — 768 dims, good for long docs
  • bge-large-en — 1024 dims, strong Chinese/multilingual support

Pros: Free, private (no API calls), fast, easy to fine-tune.

Cons: Requires local infra, slower on CPU, less quality than OpenAI, self-hosted maintenance.

3️⃣ Fine-tuned Models (Custom, domain-specific)

Fine-tune open-source models on your domain data for perfect relevance.

When to use: If you have 1000+ labeled (query, relevant_doc) pairs and retrieval quality is critical.

🎯 Recommendation for most: Start with text-embedding-3-small (OpenAI). Cost is minimal (~$0.001 per document). If you need privacy or offline, use all-mpnet-base-v2.

Embedding Dimensions: Size vs Quality

Larger embeddings capture more nuance but cost more and require more storage. Smaller are fast but lose quality.

768 dimensions (most open-source models)

Sweet spot for general use. Good quality, fast, low storage.


1024 dimensions (some large models)

Better quality for nuanced retrieval. 33% more storage. 10-15% better accuracy.

Good

1536 dimensions (OpenAI-3-small)

High quality. 2x storage cost vs 768. Best for complex domains.

Best

3072 dimensions (OpenAI-3-large)

Highest quality but 4x cost and storage. Overkill for most uses.

Overkill

Implementing Embeddings

Using OpenAI

Python
from openai import OpenAI

client = OpenAI(api_key="sk-...")

# Single embedding
response = client.embeddings.create(
    model="text-embedding-3-small",
    input="The cat sat on the mat"
)
embedding = response.data[0].embedding
print(f"Embedding size: {len(embedding)}")  # 1536

# Batch embeddings (cheaper, faster)
chunks = ["chunk 1", "chunk 2", "chunk 3"]
response = client.embeddings.create(
    model="text-embedding-3-small",
    input=chunks
)
embeddings = [d.embedding for d in response.data]

Using Open-Source (Local)

Python
from sentence_transformers import SentenceTransformer

model = SentenceTransformer('all-mpnet-base-v2')

# Single
embedding = model.encode("The cat sat on the mat")
print(f"Embedding size: {len(embedding)}")  # 768

# Batch (much faster)
chunks = ["chunk 1", "chunk 2", "chunk 3"]
embeddings = model.encode(chunks, batch_size=32)

# GPU support
embeddings = model.encode(
    chunks,
    device='cuda',  # Use GPU if available
    batch_size=32,
    show_progress_bar=True
)

Batch Processing & Scaling

Embedding 1 million documents one-at-a-time takes forever. Batch them for massive speedup.

Python
from openai import OpenAI
from tqdm import tqdm
import time

client = OpenAI()
chunks = [...]  # 1M chunks

# ❌ SLOW: One at a time
# for chunk in chunks:
#     emb = client.embeddings.create(model="...", input=chunk)

# ✓ BETTER: Batch them
batch_size = 100  # OpenAI allows up to 2048 per request
embeddings = []

for i in tqdm(range(0, len(chunks), batch_size)):
    batch = chunks[i:i+batch_size]
    response = client.embeddings.create(
        model="text-embedding-3-small",
        input=batch
    )
    embeddings.extend([d.embedding for d in response.data])
    time.sleep(1)  # Rate limiting

print(f"Embedded {len(embeddings)} chunks")

Batch Size Guidelines:

OpenAI API

Batch up to 2048 inputs per request. Larger batches are cheaper per token.

Local Models

Use 32-128 batch size depending on your GPU VRAM. Larger = faster.

Rate Limiting

OpenAI: ~3,500 requests/min for paid accounts. Space out batches with sleep().

Caching Embeddings

Don't re-embed the same chunk twice. Cache embeddings in your vector DB or use a hash-based cache.

Python
import hashlib
import json

def get_embedding(text, model="text-embedding-3-small"):
    """Get embedding, checking cache first."""
    text_hash = hashlib.md5(text.encode()).hexdigest()

    # Check if already cached
    cache_file = f"embeddings_cache/{text_hash}.json"
    try:
        with open(cache_file) as f:
            return json.load(f)
    except FileNotFoundError:
        pass

    # Generate new embedding
    response = client.embeddings.create(
        model=model,
        input=text
    )
    embedding = response.data[0].embedding

    # Cache it
    with open(cache_file, 'w') as f:
        json.dump(embedding, f)

    return embedding

💾 Better approach: Store embeddings in your vector DB directly. Once indexed, you never re-embed unless the chunk changes.

Notes

Never mix embedding models across the same index

If you embed documents with text-embedding-3-small and query with text-embedding-ada-002, cosine similarity scores become meaningless — the two models occupy completely different vector spaces. This is a silent failure: queries return results but they're semantically wrong. Lock the model name in a config constant and enforce it at both ingest and query time.

Batch your embedding API calls

OpenAI's embeddings endpoint accepts up to 2,048 inputs per request. Sending one document per call is 100× slower and exhausts rate limits quickly. Use LangChain's embed_documents() which batches automatically, or call the API directly with lists. For large corpora (> 100k chunks), use the Batch API to cut cost by 50%.

Dimension truncation is safe but has trade-offs

OpenAI's text-embedding-3 models support Matryoshka-style truncation — you can reduce from 1536 to 256 dimensions without re-training. Smaller dimensions cut storage and query latency significantly, with only minor accuracy loss on most tasks. Benchmark on your own queries before committing; accuracy loss is uneven across domains.

Cache embeddings keyed by content hash

For query-time embeddings, a simple LRU cache of (query_text → vector) eliminates redundant API calls when users ask similar questions repeatedly. For document embeddings, store vectors alongside content hashes so you can detect unchanged chunks and skip re-embedding during incremental refreshes. The savings compound quickly in long-running systems.

RAG Embeddings FAQ

What is the best embedding model for RAG?

OpenAI's text-embedding-3-small is the best cost-performance choice for cloud RAG. For local/private deployments, nomic-embed-text and bge-m3 from Hugging Face are top performers. Always evaluate on your own data with BEIR.

What embedding dimensions should I use for RAG?

OpenAI text-embedding-3-small supports 1536 dimensions (default) or truncated to 512/256. Smaller dimensions use less memory and are faster, with modest accuracy trade-offs. 768 dimensions is a common sweet spot.

Should I fine-tune embeddings for my RAG system?

Fine-tuning helps significantly when your domain has specialized vocabulary (legal, medical, code). Use the MTEB leaderboard to find a base model and fine-tune on 1,000–10,000 query-document pairs from your domain.

Can I use different embedding models for indexing and querying in RAG?

No — the embedding model for queries and documents must be identical, otherwise the vector spaces won't align and retrieval will fail. If you switch embedding models, you must re-embed and re-index all documents.

What is the cost of embedding documents for RAG?

OpenAI text-embedding-3-small costs $0.02 per million tokens. Embedding a 1,000-page PDF (~500K tokens) costs ~$0.01. Local models like sentence-transformers are free to run but require a GPU for reasonable speed.