Chapter 2: Text Embeddings & Vector Representations

Converting Text to Vectors

Learning Objectives

  • Understand text embeddings & vector representations fundamentals
  • Master the mathematical foundations
  • Learn practical implementation
  • Apply knowledge through examples
  • Recognize real-world applications

Text Embeddings & Vector Representations

Introduction: Why Convert Text to Vectors?

In RAG systems, we need to find relevant documents quickly. But how do you search through thousands or millions of documents to find the ones most relevant to a user's question? Traditional keyword search (like Google) has limitations - it can't understand that "car" and "automobile" mean the same thing, or that "Paris is the capital of France" is similar to "What is France's capital city?"

Text embeddings solve this problem by converting text into numerical vectors (arrays of numbers) that capture semantic meaning. Similar meanings result in similar vectors, allowing us to find relevant documents even when they use different words.

Real-World Analogy:

Think of embeddings like GPS coordinates for meaning. Just as two places close together on a map have similar coordinates, two texts with similar meanings have similar embedding vectors. This allows us to find "nearby" documents in meaning-space, not just word-space.

The Core Problem Embeddings Solve

Challenge: How do we enable computers to understand that these sentences are similar?

  • "What is the capital of France?"
  • "France's capital city"
  • "Where is the French capital located?"

Solution: Embeddings convert all three into vectors that are mathematically similar (high cosine similarity), even though they use different words. This enables semantic search - finding documents based on meaning, not just exact word matches.

πŸ“š Why This Matters for RAG

In RAG systems, embeddings are the foundation of retrieval. Without good embeddings, you can't find relevant documents. With good embeddings, you can:

  • Find documents even when they use synonyms or different phrasing
  • Search millions of documents in milliseconds
  • Understand semantic relationships (e.g., "machine learning" is similar to "ML" and "artificial intelligence")
  • Handle multilingual content if using multilingual embedding models

Key Concepts

What Are Vector Embeddings?

Vector embeddings are dense numerical representations of text that capture semantic meaning. They convert words, sentences, or documents into fixed-size arrays of numbers (vectors) where similar meanings result in similar vectors.

Understanding Embeddings with an Analogy

Think of embeddings like coordinates on a map of meaning:

  • Texts about "France" might be at coordinates [0.2, -0.5, 0.8, ...]
  • Texts about "Germany" might be at [0.3, -0.4, 0.7, ...] (close, since both are European countries)
  • Texts about "Python programming" might be at [-0.1, 0.9, -0.3, ...] (far away, different topic)

Just as you can measure distance between GPS coordinates, you can measure similarity between embedding vectors using cosine similarity or Euclidean distance.

Key Properties of Embeddings

1. Fixed Dimension:

  • Each embedding has a fixed number of dimensions (typically 384, 768, or 1536)
  • All texts are converted to vectors of the same size, enabling mathematical operations
  • Example: "Hello world" and "The entire history of human civilization" both become 384-dimensional vectors

2. Semantic Similarity = Vector Similarity:

  • Texts with similar meanings have vectors that are close together in the high-dimensional space
  • We measure this using cosine similarity: similar texts have high cosine similarity (close to 1.0)
  • Example: "car" and "automobile" have high similarity, even though they're different words

3. Vector Arithmetic:

  • Embeddings can capture relationships through vector arithmetic
  • Famous example: "king" - "man" + "woman" β‰ˆ "queen"
  • This shows embeddings capture semantic relationships, not just word similarity

4. Enable Efficient Similarity Search:

  • Once text is in vector form, we can use mathematical operations to find similar texts
  • This enables fast semantic search across millions of documents
  • Vector databases can find similar vectors in milliseconds

How Embeddings Capture Meaning

Embeddings work because they're trained on massive amounts of text data. The model learns patterns like:

  • Words that appear in similar contexts (e.g., "doctor" and "nurse" both appear near "hospital") should have similar vectors
  • Sentences with similar meanings should be close in vector space
  • Semantic relationships (synonyms, antonyms, related concepts) are encoded in the vector positions
Example: How Embeddings Understand Similarity

Consider these three sentences:

  1. "The capital of France is Paris"
  2. "Paris is the capital city of France"
  3. "The weather in Tokyo is rainy today"

After embedding:

  • Sentences 1 and 2 will have very similar vectors (high cosine similarity, e.g., 0.95)
  • Sentence 3 will have a very different vector (low similarity, e.g., 0.15)

This allows RAG systems to find sentence 1 or 2 when a user asks "What is France's capital?" even if the exact wording doesn't match.

What Are Embedding Models?

Embedding models are neural networks trained to convert text into dense vector representations. They learn to map semantically similar texts to nearby points in a high-dimensional vector space (typically 384-1536 dimensions).

How Embedding Models Work

Embedding models are trained on massive text corpora (billions of sentences) to learn that:

  • Words that appear in similar contexts should have similar vectors
  • Sentences with similar meanings should be close in vector space
  • Semantic relationships (like "king" - "man" + "woman" β‰ˆ "queen") should be preserved

Training process: Models learn by predicting masked words, next sentences, or by contrasting similar vs. dissimilar sentence pairs. Through this training, they develop an internal "understanding" of language that gets encoded in the vector representations.

Types of Embedding Models

1. Sentence Transformers

What they are: Models specifically optimized for creating embeddings of entire sentences or paragraphs, not just individual words.

Why we use them: They're designed for semantic similarity tasks and work excellently for RAG retrieval. They're fast, efficient, and produce high-quality embeddings.

Examples:

  • all-MiniLM-L6-v2: 384 dimensions, fast and efficient, good for most use cases
  • all-mpnet-base-v2: 768 dimensions, higher quality but slower
  • multi-qa-MiniLM-L6-cos-v1: Optimized for question-answering tasks

When to use: General-purpose RAG systems, when you need fast inference, or when working with sentence/paragraph-level documents.

2. BERT-Based Embeddings

What they are: Embeddings derived from BERT (Bidirectional Encoder Representations from Transformers) models. These are contextual embeddings that consider the full sentence context.

Why we use them: They capture rich contextual information and understand word meanings based on surrounding text.

Examples: BERT-base, RoBERTa, DistilBERT

When to use: When you need high-quality embeddings and can handle slower inference, or when working with domain-specific content.

3. OpenAI Embeddings

What they are: Commercial embedding models provided by OpenAI via API.

Why we use them: High quality, well-optimized, and easy to use via API. No need to host models yourself.

Examples:

  • text-embedding-ada-002: 1536 dimensions, OpenAI's current recommended model
  • text-embedding-3-small: 1536 dimensions, newer and more efficient
  • text-embedding-3-large: 3072 dimensions, highest quality

When to use: When you want high-quality embeddings without managing model infrastructure, or when building production systems where API costs are acceptable.

4. Domain-Specific Embeddings

What they are: Embedding models fine-tuned on specific domains (medical, legal, scientific, etc.)

Why we use them: They understand domain-specific terminology and relationships better than general models.

Examples:

  • BioBERT for biomedical texts
  • Legal-BERT for legal documents
  • SciBERT for scientific papers

When to use: When working with specialized domains where general models struggle, or when domain terminology is critical for retrieval quality.

How to Choose an Embedding Model

Consider these factors:

  • Quality vs. Speed: Larger models (768-1536 dims) are higher quality but slower. Smaller models (384 dims) are faster but may sacrifice some quality.
  • Domain: Use domain-specific models if available for your use case.
  • Language: For multilingual content, use multilingual models (e.g., multilingual-MiniLM).
  • Infrastructure: API-based (OpenAI) vs. self-hosted (SentenceTransformers) - consider costs and latency.
  • Evaluation: Test multiple models on your specific data and use cases to find the best fit.

What Are Vector Databases and Why Do We Need Them?

The Problem Vector Databases Solve

Imagine you have 1 million documents, each with a 384-dimensional embedding vector. When a user asks a question, you need to:

  1. Embed the query (get a 384-dim vector)
  2. Compare this query vector with all 1 million document vectors
  3. Find the top 5 most similar documents

The challenge: Computing cosine similarity between the query and all 1 million documents would require 1 million vector operations. Even if each takes 0.001 seconds, that's 1000 seconds (16+ minutes) - way too slow for a real-time system!

Vector databases solve this by using specialized indexing algorithms (like HNSW - Hierarchical Navigable Small World) that can find similar vectors in milliseconds, even with millions of documents.

What Is a Vector Database?

A vector database is a specialized database designed to store and efficiently search high-dimensional vectors (embeddings). Unlike traditional databases that search by exact matches or keywords, vector databases search by similarity in vector space.

Traditional Database vs. Vector Database

Traditional SQL Database:

  • Stores: Structured data (names, dates, numbers)
  • Searches: Exact matches, ranges, joins
  • Query: "SELECT * WHERE name = 'John'"
  • Problem: Can't search by semantic similarity

Vector Database:

  • Stores: High-dimensional vectors (embeddings)
  • Searches: Similarity search (find nearest neighbors)
  • Query: "Find vectors most similar to [0.2, -0.5, 0.8, ...]"
  • Solution: Fast semantic similarity search

Why Do We Need Vector Databases in RAG?

1. Speed: Vector databases use Approximate Nearest Neighbor (ANN) algorithms that can search millions of vectors in milliseconds, compared to minutes with brute-force search.

2. Scalability: As your knowledge base grows from thousands to millions of documents, vector databases maintain fast query times. Traditional methods would become prohibitively slow.

3. Efficiency: Vector databases are optimized for the specific task of similarity search. They use techniques like:

  • HNSW (Hierarchical Navigable Small World): Creates a graph structure where similar vectors are connected, enabling fast navigation to nearest neighbors
  • IVF (Inverted File Index): Groups similar vectors into clusters, then searches only relevant clusters
  • Product Quantization: Compresses vectors to reduce memory and speed up search

4. Metadata Filtering: Vector databases allow you to combine similarity search with traditional filtering. For example: "Find documents similar to this query, but only from 2024, and only in the 'legal' category."

When Do We Use Vector Databases?

βœ… Use Vector Databases When:
  • Large-scale systems: You have thousands or millions of documents
  • Real-time requirements: You need sub-second query responses
  • Production systems: You need reliability, scalability, and managed infrastructure
  • Complex queries: You need metadata filtering combined with similarity search
  • Growing knowledge base: Your document collection will expand over time
❌ You Might Skip Vector Databases When:
  • Small datasets: You have fewer than 1,000 documents (NumPy arrays might be sufficient)
  • Prototyping: You're building a proof-of-concept and speed isn't critical
  • Simple use cases: You don't need advanced features like metadata filtering
  • Budget constraints: Managed vector databases have costs (though open-source options exist)

Popular Vector Database Options

1. Pinecone

What it is: Fully managed, cloud-based vector database service

Why use it: Zero infrastructure management, automatic scaling, high performance, built-in security

Best for: Production systems, teams without DevOps resources, applications requiring high reliability

Considerations: Paid service (though has free tier), requires internet connection

2. Weaviate

What it is: Open-source vector database with optional cloud hosting

Why use it: Self-hostable, GraphQL API, built-in vectorization, good documentation

Best for: Teams comfortable with self-hosting, need flexibility, want open-source solution

Considerations: Requires infrastructure management if self-hosting

3. Chroma

What it is: Lightweight, open-source vector database designed for simplicity

Why use it: Easy to use, Python-first, good for prototyping and small-to-medium scale

Best for: Prototyping, Python-heavy projects, smaller datasets, getting started quickly

Considerations: May not scale as well as others for very large datasets

4. FAISS (Facebook AI Similarity Search)

What it is: Library for efficient similarity search, not a full database

Why use it: Extremely fast, open-source, used by Facebook at scale, in-memory or on-disk

Best for: When you need maximum performance, have technical expertise, want to build custom solutions

Considerations: Lower-level API, requires more setup, no built-in persistence (you handle it)

5. Qdrant

What it is: Open-source vector database with cloud option

Why use it: High performance, good filtering capabilities, REST and gRPC APIs

Best for: Production systems needing high performance, teams wanting open-source with cloud option

Considerations: Requires infrastructure if self-hosting

How Vector Databases Work in RAG

Step 1 - Indexing (One-time):

  1. Embed all documents using your embedding model
  2. Store document embeddings in the vector database
  3. Vector database builds an index (e.g., HNSW graph) for fast search
  4. Store metadata (document ID, title, date, etc.) alongside embeddings

Step 2 - Querying (Per Query):

  1. Embed the user query
  2. Query the vector database: "Find top-k vectors most similar to query embedding"
  3. Vector database uses its index to quickly find similar vectors (milliseconds)
  4. Return document IDs and metadata for the top-k matches
  5. Retrieve actual document text using the IDs

Performance comparison:

  • Brute-force (NumPy): 1M documents = ~16 minutes
  • Vector Database (HNSW): 1M documents = ~50-200 milliseconds
  • Speedup: ~5,000-20,000x faster!

Mathematical Formulations

Similarity and Distance Metrics Overview

In RAG systems, we need to measure how similar two embedding vectors are. Different metrics serve different purposes and have different properties. Here are the most important ones used in production RAG systems:

Embedding Function

\[E: \text{text} \rightarrow \mathbb{R}^d\]
What This Represents:

This function maps any text input to a d-dimensional real-valued vector. The embedding model \(E\) learns this mapping during training.

Where:
  • \(E\): Embedding model (e.g., SentenceTransformer, BERT)
  • \(\text{text}\): Input text string (word, sentence, or document)
  • \(\mathbb{R}^d\): d-dimensional real vector space
  • Typical d: 384 (fast), 768 (balanced), 1536 (high quality)
Example:

\(E(\text{"machine learning"}) = [0.23, -0.45, 0.67, ..., 0.12] \in \mathbb{R}^{384}\)

1. Cosine Similarity (Most Common in RAG)

\[\text{similarity}(v_1, v_2) = \frac{v_1 \cdot v_2}{\|v_1\| \|v_2\|} = \cos(\theta)\]
What This Measures:

Cosine similarity measures the cosine of the angle between two vectors. It focuses on direction rather than magnitude, making it ideal for comparing embeddings where the length doesn't matter.

Breaking It Down:
  • \(v_1 \cdot v_2\): Dot product (sum of element-wise products)
  • \(\|v_1\|\): Magnitude (norm) of vector \(v_1 = \sqrt{\sum_{i=1}^{d} v_{1i}^2}\)
  • \(\|v_2\|\): Magnitude of vector \(v_2\)
  • \(\theta\): Angle between the two vectors
Properties:
  • Range: [-1, 1], typically [0, 1] for normalized embeddings
  • Scale-invariant: Only cares about direction, not magnitude
  • Interpretation: 1.0 = identical direction, 0 = orthogonal, -1 = opposite
Why It's Preferred in RAG:
  • Works well with normalized embeddings (most embedding models produce normalized vectors)
  • Focuses on semantic similarity (direction) rather than vector magnitude
  • Handles documents of different lengths well
  • Fast to compute
When to Use:

βœ… Use cosine similarity when: Working with normalized embeddings, comparing semantic similarity, documents vary in length, or using most modern embedding models.

2. Dot Product (Inner Product)

\[\text{dot\_product}(v_1, v_2) = v_1 \cdot v_2 = \sum_{i=1}^{d} v_{1i} \times v_{2i}\]
What This Measures:

The dot product is the sum of element-wise products of two vectors. It measures both direction AND magnitude, unlike cosine similarity which only measures direction.

Breaking It Down:
  • For each dimension \(i\), multiply \(v_{1i} \times v_{2i}\)
  • Sum all these products: \(\sum_{i=1}^{d} v_{1i} \times v_{2i}\)
  • Result is a scalar value (single number)
Properties:
  • Range: Unbounded (can be any real number)
  • Magnitude-sensitive: Larger vectors produce larger dot products
  • Relationship to cosine: If vectors are normalized, dot product = cosine similarity
When to Use:
  • βœ… Use dot product when: Embeddings are already normalized (then it's equivalent to cosine), you need maximum speed (slightly faster than cosine), or using FAISS with inner product index
  • ❌ Avoid when: Embeddings aren't normalized, or you want magnitude-independent similarity
Example:

If \(v_1 = [0.5, 0.3, 0.8]\) and \(v_2 = [0.4, 0.6, 0.7]\), then:

\(v_1 \cdot v_2 = (0.5 \times 0.4) + (0.3 \times 0.6) + (0.8 \times 0.7) = 0.2 + 0.18 + 0.56 = 0.94\)

3. Euclidean Distance (L2 Distance)

\[d_{\text{euclidean}}(v_1, v_2) = \|v_1 - v_2\|_2 = \sqrt{\sum_{i=1}^{d} (v_{1i} - v_{2i})^2}\]
What This Measures:

Euclidean distance measures the straight-line distance between two points in vector space. It's the most intuitive distance measure - like measuring distance on a map.

Breaking It Down:
  • For each dimension \(i\), compute the difference: \(v_{1i} - v_{2i}\)
  • Square each difference: \((v_{1i} - v_{2i})^2\)
  • Sum all squared differences: \(\sum_{i=1}^{d} (v_{1i} - v_{2i})^2\)
  • Take the square root: \(\sqrt{\sum_{i=1}^{d} (v_{1i} - v_{2i})^2}\)
Properties:
  • Range: [0, ∞) - always non-negative
  • Interpretation: Lower distance = more similar vectors
  • Magnitude-sensitive: Affected by vector magnitudes
  • Metric: Satisfies triangle inequality
Relationship to Cosine Similarity:

For normalized vectors, Euclidean distance and cosine similarity are related:

If \(\|v_1\| = \|v_2\| = 1\), then: \(d_{\text{euclidean}}^2 = 2(1 - \cos(\theta))\)

This means: lower Euclidean distance = higher cosine similarity (for normalized vectors)

When to Use:
  • βœ… Use Euclidean distance when: You want to consider both direction and magnitude, working with non-normalized embeddings, or using clustering algorithms that require distance metrics
  • ❌ Less common in RAG: Cosine similarity is usually preferred for semantic search
Example:

If \(v_1 = [1, 2, 3]\) and \(v_2 = [4, 5, 6]\), then:

\(d = \sqrt{(1-4)^2 + (2-5)^2 + (3-6)^2} = \sqrt{9 + 9 + 9} = \sqrt{27} \approx 5.20\)

4. Manhattan Distance (L1 Distance)

\[d_{\text{manhattan}}(v_1, v_2) = \|v_1 - v_2\|_1 = \sum_{i=1}^{d} |v_{1i} - v_{2i}|\]
What This Measures:

Manhattan distance (also called L1 distance or taxicab distance) measures the sum of absolute differences along each dimension. It's like measuring distance in a city with a grid layout - you can only move along streets, not diagonally.

Breaking It Down:
  • For each dimension \(i\), compute the absolute difference: \(|v_{1i} - v_{2i}|\)
  • Sum all absolute differences: \(\sum_{i=1}^{d} |v_{1i} - v_{2i}|\)
  • No squaring or square root needed
Properties:
  • Range: [0, ∞) - always non-negative
  • Interpretation: Lower distance = more similar
  • Robust to outliers: Less sensitive to large differences in individual dimensions (compared to Euclidean)
  • Faster computation: No squaring or square root operations
When to Use:
  • βœ… Use Manhattan distance when: You have sparse embeddings, want robustness to outliers, need very fast computation, or working with high-dimensional data where Euclidean distance becomes less meaningful
  • ❌ Less common in RAG: Cosine similarity is typically preferred for semantic similarity
Example:

If \(v_1 = [1, 2, 3]\) and \(v_2 = [4, 5, 6]\), then:

\(d = |1-4| + |2-5| + |3-6| = 3 + 3 + 3 = 9\)

5. Minkowski Distance (Generalization)

\[d_{\text{minkowski}}(v_1, v_2, p) = \left(\sum_{i=1}^{d} |v_{1i} - v_{2i}|^p\right)^{1/p}\]
What This Represents:

Minkowski distance is a generalization that includes both Euclidean and Manhattan distances as special cases. The parameter \(p\) controls the type of distance.

Special Cases:
  • \(p = 1\): Manhattan distance (L1): \(d = \sum |v_{1i} - v_{2i}|\)
  • \(p = 2\): Euclidean distance (L2): \(d = \sqrt{\sum (v_{1i} - v_{2i})^2}\)
  • \(p \to \infty\): Chebyshev distance (L∞): \(d = \max_i |v_{1i} - v_{2i}|\)
Properties:
  • Flexibility: Can tune \(p\) to balance between Manhattan (p=1) and Euclidean (p=2) behaviors
  • Higher p: More emphasis on large differences in individual dimensions
  • Lower p: More robust to outliers, treats all dimensions more equally
When to Use:
  • βœ… Use Minkowski distance when: You need to experiment with different distance metrics, want to tune the sensitivity to outliers, or working on research/optimization problems
  • ❌ Less common in production RAG: Cosine similarity is the standard choice

6. Normalized Dot Product (For Non-Normalized Embeddings)

\[\text{normalized\_dot}(v_1, v_2) = \frac{v_1 \cdot v_2}{\|v_1\| \|v_2\|} = \cos(\theta)\]
What This Is:

This is actually the same as cosine similarity! When you normalize the dot product by dividing by the vector magnitudes, you get cosine similarity. This formula shows the relationship explicitly.

Key Insight:

If your embeddings are already normalized (most modern models produce normalized embeddings), then:

  • \(\|v_1\| = \|v_2\| = 1\)
  • Normalized dot product = dot product = cosine similarity
  • This is why many vector databases use dot product internally (it's faster) when embeddings are normalized

Which Metric Should You Use in RAG?

πŸ₯‡ Recommended: Cosine Similarity

Why: Most embedding models produce normalized vectors, and cosine similarity focuses on semantic direction rather than magnitude. It's the standard in production RAG systems.

When: Default choice for most RAG applications, especially with SentenceTransformers, OpenAI embeddings, or BERT-based models.

πŸ₯ˆ Alternative: Dot Product (for normalized embeddings)

Why: Mathematically equivalent to cosine similarity for normalized vectors, but slightly faster to compute (no division needed).

When: When embeddings are guaranteed to be normalized and you need maximum speed, or when using FAISS with inner product index.

πŸ₯‰ Special Cases: Euclidean or Manhattan

Why: Useful when magnitude matters, or when working with non-normalized embeddings.

When: Clustering applications, when vector magnitude carries important information, or when experimenting with different metrics.

Performance Comparison

Metric Speed Magnitude-Sensitive Common in RAG
Cosine Similarity Fast No βœ… Most common
Dot Product Fastest Yes (unless normalized) βœ… Common (when normalized)
Euclidean Distance Medium Yes ⚠️ Less common
Manhattan Distance Fast Yes ❌ Rare

Detailed Examples

Example 1: Converting Text to Embeddings - Step by Step

Scenario: You have a document about machine learning and want to convert it to an embedding vector for storage in a vector database.

Input text: "Machine learning is a subset of artificial intelligence that enables computers to learn from data without explicit programming."

Step 1: Preprocessing

  • Text is cleaned and normalized (handling special characters, whitespace)
  • Result: Clean text ready for tokenization

Step 2: Tokenization

  • The sentence is split into tokens (subwords or words depending on the model)
  • Example tokens: ["Machine", "learning", "is", "a", "subset", "of", "artificial", "intelligence", ...]
  • Each token is mapped to a token ID from the model's vocabulary

Step 3: Model Processing

  • Token IDs are passed through the transformer model (e.g., SentenceTransformer)
  • The model processes the entire sentence, using attention mechanisms to understand relationships between words
  • Hidden states are generated for each token position

Step 4: Pooling

  • Token-level embeddings are pooled (typically mean pooling) to create a single sentence-level embedding
  • This creates a fixed-size vector regardless of sentence length

Step 5: Normalization

  • The embedding vector is normalized (L2 normalization) so its magnitude is 1
  • This makes cosine similarity equivalent to dot product

Final Output:

  • 384-dimensional vector: [0.23, -0.45, 0.67, 0.12, -0.34, ..., 0.89]
  • Each dimension captures some aspect of semantic meaning
  • This vector can now be stored in a vector database

Example 2: Semantic Similarity in Action

Scenario: Demonstrating how embeddings capture semantic meaning, not just word matching.

Document 1: "The capital of France is Paris"
Embedding: [0.24, -0.44, 0.66, 0.12, ...]

Document 2: "Paris is the capital city of France"
Embedding: [0.25, -0.43, 0.65, 0.11, ...]

Document 3: "The weather in Paris is sunny today"
Embedding: [0.15, 0.22, -0.18, 0.45, ...]

Query: "What is the capital of France?"
Query Embedding: [0.24, -0.44, 0.66, 0.12, ...]

Similarity Calculations:

  • Query vs Doc 1: Cosine similarity = 0.98 (almost identical - same meaning, different word order)
  • Query vs Doc 2: Cosine similarity = 0.97 (very similar - paraphrased but same meaning)
  • Query vs Doc 3: Cosine similarity = 0.42 (different topic - about weather, not capitals)

Key Insight: Even though Doc 1 and Doc 2 use different word orders and slightly different phrasing, they have very similar embeddings because they convey the same semantic meaning. Doc 3 has a different embedding because it's about a different topic (weather vs. geography).

Example 3: Complete Embedding and Retrieval Workflow

Scenario: Building a knowledge base and querying it using embeddings.

Step 1: Document Indexing

You have 3 documents to index:

  • Doc 1: "Machine learning uses algorithms to learn from data"
  • Doc 2: "Deep learning is a subset of machine learning using neural networks"
  • Doc 3: "Python is a popular programming language for data science"

Step 2: Generate Embeddings

  • Doc 1 embedding: [0.45, -0.23, 0.67, ..., 0.12]
  • Doc 2 embedding: [0.48, -0.25, 0.65, ..., 0.14]
  • Doc 3 embedding: [0.12, 0.34, -0.21, ..., -0.45]

Step 3: Store in Vector Database

  • All three embeddings are stored in the vector database with their document IDs
  • An index (e.g., HNSW) is built for fast similarity search

Step 4: Query Processing

User asks: "What is machine learning?"

  • Query embedding: [0.46, -0.24, 0.66, ..., 0.13]

Step 5: Similarity Search

  • Compare query embedding with all document embeddings:
  • Query vs Doc 1: similarity = 0.95 (very high - directly about machine learning)
  • Query vs Doc 2: similarity = 0.92 (high - related, mentions machine learning)
  • Query vs Doc 3: similarity = 0.35 (low - about Python, not machine learning)

Step 6: Top-k Retrieval

  • Retrieve top-2: Doc 1 and Doc 2 (highest similarity scores)
  • These documents are passed to the LLM as context

Result: The system successfully finds documents about machine learning, even though the query uses slightly different wording than the documents.

Example 4: Embedding Dimension Comparison

Scenario: Comparing embeddings of different dimensions to understand the trade-offs.

Same text: "Neural networks are used for deep learning"

384-dimensional embedding (all-MiniLM-L6-v2):

  • Size: 384 values
  • Storage: ~1.5 KB per embedding
  • Speed: Fast (quick to compute and compare)
  • Quality: Good for most use cases
  • Example: [0.23, -0.45, 0.67, ..., 0.12] (384 values)

768-dimensional embedding (all-mpnet-base-v2):

  • Size: 768 values
  • Storage: ~3 KB per embedding (2x larger)
  • Speed: Slower (more computation)
  • Quality: Higher (better semantic understanding)
  • Example: [0.23, -0.45, 0.67, 0.12, ..., 0.89] (768 values)

1536-dimensional embedding (OpenAI text-embedding-ada-002):

  • Size: 1536 values
  • Storage: ~6 KB per embedding (4x larger than 384-dim)
  • Speed: Slower (requires API call)
  • Quality: Highest (best semantic understanding)
  • Example: [0.23, -0.45, 0.67, ..., 0.12] (1536 values)

Trade-off Analysis:

For 1 million documents:

  • 384-dim: 1.5 GB storage, fast search, good quality
  • 768-dim: 3 GB storage, slower search, better quality
  • 1536-dim: 6 GB storage, slowest (API), best quality

Recommendation: Start with 384-dim for speed, upgrade to 768-dim if quality is insufficient, use 1536-dim only if quality is critical and you can afford the cost/latency.

Example 5: Handling Synonyms and Paraphrasing

Scenario: Demonstrating how embeddings handle synonyms and different phrasings.

Query: "How do I train a neural network?"

Document 1: "Training deep learning models requires adjusting hyperparameters"
Note: Uses "deep learning models" instead of "neural network", and "training" instead of "train"

Document 2: "Neural networks are trained using backpropagation"
Note: Uses exact phrase "neural network" and "trained"

Document 3: "The weather forecast predicts rain tomorrow"
Note: Completely unrelated topic

Similarity Results:

  • Query vs Doc 1: 0.88 (high - understands "deep learning models" β‰ˆ "neural network")
  • Query vs Doc 2: 0.92 (very high - exact terminology match)
  • Query vs Doc 3: 0.15 (very low - completely different topic)

Key Insight: Embeddings understand that "neural network" and "deep learning model" are semantically similar concepts, so Doc 1 is retrieved even though it doesn't contain the exact phrase "neural network". This is the power of semantic search - it finds relevant documents even when they use different terminology.

Implementation

Implementation Overview

This section provides practical Python code examples for creating embeddings and using vector databases in RAG systems. The examples use popular libraries: SentenceTransformers for embeddings and Chroma for vector storage.

1. Creating Embeddings with SentenceTransformers

What this does: Converts text documents into dense vector embeddings that capture semantic meaning, enabling similarity search.

from sentence_transformers import SentenceTransformer
import numpy as np

# Load embedding model
# 'all-MiniLM-L6-v2' is a popular, fast model (384 dimensions)
# Other options: 'all-mpnet-base-v2' (768 dims, higher quality)
model = SentenceTransformer('all-MiniLM-L6-v2')

# Create embeddings for multiple documents
texts = [
    "The capital of France is Paris",
    "Germany's capital is Berlin",
    "Italy is a country in Europe"
]

# Encode all texts at once (batch processing is more efficient)
embeddings = model.encode(texts, show_progress_bar=True)
print(f"Embedding shape: {embeddings.shape}")  # (3, 384)
# Output: 3 documents, each with 384-dimensional vector

# Create query embedding
query = "What is the capital of France?"
query_embedding = model.encode([query])  # Shape: (1, 384)

# Compute cosine similarities
# For normalized embeddings, dot product = cosine similarity
similarities = np.dot(embeddings, query_embedding.T).flatten()
print(f"Similarities: {similarities}")
# Output: [0.95, 0.45, 0.32] (example values)
# Higher values = more similar

# Get most similar document
most_similar_idx = np.argmax(similarities)
print(f"Most similar: {texts[most_similar_idx]}")
# Output: "The capital of France is Paris"

# Get top-k most similar
top_k = 2
top_k_indices = np.argsort(similarities)[-top_k:][::-1]  # Sort descending
print(f"Top {top_k} most similar:")
for idx in top_k_indices:
    print(f"  {texts[idx]} (similarity: {similarities[idx]:.3f})")
Key Points:
  • Model selection: all-MiniLM-L6-v2 is fast and good for most use cases. Use all-mpnet-base-v2 for higher quality.
  • Batch encoding: Always encode multiple texts together for efficiency (faster than one-by-one).
  • Normalized embeddings: SentenceTransformers produces normalized embeddings, so dot product equals cosine similarity.
  • Similarity scores: Range from -1 to 1, typically 0 to 1 for normalized embeddings. Higher = more similar.

2. Using Vector Database (Chroma)

What this does: Stores document embeddings in a vector database for efficient similarity search across large document collections.

import chromadb
from chromadb.config import Settings

# Initialize Chroma client
# For production, use PersistentClient to save data to disk
client = chromadb.PersistentClient(path="./chroma_db")

# Or use in-memory client for testing
# client = chromadb.Client(Settings())

# Create or get a collection
# Collections are like tables in traditional databases
collection = client.get_or_create_collection(
    name="documents",
    metadata={"hnsw:space": "cosine"}  # Use cosine similarity
)

# Add documents with metadata
documents = [
    "The capital of France is Paris",
    "Germany's capital is Berlin",
    "Italy's capital is Rome"
]

# Metadata for filtering (optional but recommended)
metadatas = [
    {"country": "France", "type": "capital"},
    {"country": "Germany", "type": "capital"},
    {"country": "Italy", "type": "capital"}
]

ids = ["doc1", "doc2", "doc3"]

# Add to collection
# Chroma automatically generates embeddings using default model
# Or you can provide your own embeddings
collection.add(
    documents=documents,
    metadatas=metadatas,
    ids=ids
)

# Query the collection
query = "What is the capital of France?"
results = collection.query(
    query_texts=[query],
    n_results=2,  # Return top 2 most similar
    # Optional: filter by metadata
    # where={"country": "France"}
)

# Access results
print("Retrieved documents:", results['documents'][0])
print("Similarity distances:", results['distances'][0])
print("Document IDs:", results['ids'][0])
print("Metadata:", results['metadatas'][0])

# Output example:
# Retrieved documents: ['The capital of France is Paris', 'Germany's capital is Berlin']
# Similarity distances: [0.12, 0.45]  # Lower = more similar (distance, not similarity)
# Document IDs: ['doc1', 'doc2']
# Metadata: [{'country': 'France', 'type': 'capital'}, {'country': 'Germany', 'type': 'capital'}]
Key Points:
  • Persistent storage: Use PersistentClient in production to save data to disk.
  • Collections: Organize documents into collections (like tables).
  • Metadata filtering: Store metadata (date, category, etc.) to enable filtering before similarity search.
  • Automatic embeddings: Chroma can generate embeddings automatically, or you can provide your own.
  • Distance vs similarity: Chroma returns distances (lower = more similar), not similarity scores.

3. Complete RAG Example: Embedding + Vector DB + Similarity Search

What this does: A complete example combining embedding generation, vector database storage, and similarity search for a RAG system.

from sentence_transformers import SentenceTransformer
import chromadb
import numpy as np

# Step 1: Initialize embedding model
embedding_model = SentenceTransformer('all-MiniLM-L6-v2')

# Step 2: Initialize vector database
client = chromadb.PersistentClient(path="./rag_db")
collection = client.get_or_create_collection(
    name="knowledge_base",
    metadata={"hnsw:space": "cosine"}
)

# Step 3: Prepare documents (in real RAG, these come from your knowledge base)
documents = [
    "Machine learning is a subset of artificial intelligence.",
    "Deep learning uses neural networks with multiple layers.",
    "Natural language processing enables computers to understand text.",
    "Computer vision allows machines to interpret visual information."
]

# Step 4: Generate embeddings
embeddings = embedding_model.encode(documents, show_progress_bar=True)

# Step 5: Add to vector database with custom embeddings
ids = [f"doc_{i}" for i in range(len(documents))]
collection.add(
    embeddings=embeddings.tolist(),  # Convert numpy array to list
    documents=documents,
    ids=ids
)

# Step 6: Query the knowledge base
user_query = "What is machine learning?"
query_embedding = embedding_model.encode([user_query])

# Retrieve top-k most similar documents
results = collection.query(
    query_embeddings=query_embedding.tolist(),
    n_results=2
)

# Step 7: Use retrieved context for RAG
retrieved_docs = results['documents'][0]
print("Retrieved context:")
for i, doc in enumerate(retrieved_docs, 1):
    print(f"{i}. {doc}")

# In a real RAG system, you would:
# 1. Combine retrieved_docs into a prompt
# 2. Send prompt + query to LLM (OpenAI, Anthropic, etc.)
# 3. Return LLM's generated answer

# Example prompt construction:
context = "\n\n".join(retrieved_docs)
prompt = f"""Context:
{context}

Question: {user_query}

Answer:"""
print("\nConstructed prompt:")
print(prompt)
Complete RAG Pipeline:
  1. Document Processing: Load and chunk documents from your knowledge base
  2. Embedding Generation: Convert chunks to embeddings (this example)
  3. Vector Storage: Store embeddings in vector database (this example)
  4. Query Processing: Embed user query and retrieve similar documents (this example)
  5. Context Assembly: Combine retrieved documents into prompt (this example)
  6. LLM Generation: Send prompt to LLM to generate answer (not shown - requires LLM API)

Installation Requirements

To run these examples, install the required packages:

pip install sentence-transformers chromadb numpy

Alternative Vector Databases

While this example uses Chroma, you can use other vector databases with similar APIs:

  • Pinecone: Managed cloud service, easy setup, good for production
  • Weaviate: Self-hosted or cloud, supports GraphQL, good for complex queries
  • Qdrant: Fast, supports filtering, good for high-performance applications
  • FAISS: Facebook's library, good for research and self-hosted solutions

Real-World Applications

1. Semantic Search in E-Commerce - Real-World Example

Company: Large online marketplace (similar to Etsy, eBay)

Problem: Customers search for products using various terms. A search for "comfortable running shoes" might not find products labeled "athletic footwear" or "sneakers for jogging" even though they're the same thing. Traditional keyword search missed many relevant products.

Solution with Embeddings:

  • Product Descriptions: All 10 million product descriptions were converted to embeddings using sentence transformers
  • Search Process: When a customer searches "comfortable running shoes", the query is embedded and compared with all product embeddings using cosine similarity
  • Results:
    • βœ… 40% increase in relevant product discovery
    • βœ… Customers find products even when they use different terminology than sellers
    • βœ… Better search experience - understands intent, not just keywords
    • βœ… Increased sales - customers find more relevant products

Why Embeddings Work: Embeddings capture semantic meaning. "Running shoes", "athletic footwear", and "jogging sneakers" have similar embeddings even though they use different words. This enables semantic search that understands user intent.

2. Multilingual Customer Support - Real-World Example

Company: Global SaaS company with customers in 50+ countries

Problem: Support team receives questions in multiple languages. English support documentation exists, but customers ask questions in their native languages. Translating all documentation to 20+ languages was expensive and time-consuming.

Solution with Multilingual Embeddings:

  • Multilingual Model: Used multilingual-MiniLM-L6-v2 embedding model that understands 50+ languages
  • Knowledge Base: Indexed English documentation (didn't need translation!)
  • Query Processing: Customer questions in any language are embedded and matched against English documentation embeddings
  • Results:
    • βœ… Support for 20+ languages without translating documentation
    • βœ… 85% accuracy in finding relevant English docs for non-English queries
    • βœ… Cost savings: $500K+ saved on translation services
    • βœ… Faster support - answers available immediately in any language

Example: A customer asks in Spanish: "ΒΏCΓ³mo cambio mi contraseΓ±a?" (How do I change my password?). The system embeds this query, finds the English "Password Reset Guide" document (because embeddings capture semantic meaning across languages), retrieves it, and generates an answer in Spanish.

Why Multilingual Embeddings Work: Multilingual embedding models are trained on parallel text in multiple languages. They learn that "password" and "contraseΓ±a" have similar meanings, enabling cross-lingual semantic search.

3. Content Recommendation System - Real-World Example

Company: Online learning platform (similar to Coursera, Udemy)

Problem: Platform has 50,000+ courses. Students need help finding courses relevant to their interests and skill level. Traditional keyword-based recommendations often missed relevant courses.

Solution with Embeddings:

  • Course Descriptions: All course descriptions, learning objectives, and syllabus content were embedded
  • Student Profiles: Student interests, completed courses, and learning goals were also embedded
  • Recommendation: System finds courses with similar embeddings to student interests
  • Results:
    • βœ… 35% increase in course enrollments
    • βœ… Better course discovery - students find courses they didn't know existed
    • βœ… Higher completion rates - students enroll in more relevant courses
    • βœ… Improved student satisfaction

Example: A student interested in "machine learning for beginners" is recommended courses on "introductory AI", "data science fundamentals", and "neural networks basics" - all semantically similar even though they use different terminology.

4. Document Similarity Detection - Real-World Example

Company: Academic publishing platform

Problem: Platform receives thousands of research paper submissions. Editors need to identify similar papers, detect potential plagiarism, and find related work. Manual comparison is time-consuming and error-prone.

Solution with Embeddings:

  • Paper Embeddings: All submitted papers are embedded (abstract, introduction, methodology sections)
  • Similarity Detection: New submissions are embedded and compared with existing papers using cosine similarity
  • Results:
    • βœ… 90% accuracy in detecting similar papers (vs. 60% with keyword matching)
    • βœ… 10x faster than manual review
    • βœ… Identifies related work that editors might miss
    • βœ… Helps detect plagiarism and duplicate submissions

Why Embeddings Work: Embeddings capture semantic meaning, so they can identify papers with similar ideas even when they use different terminology or writing styles. This is much more effective than keyword matching for academic content.

Choosing the Right Embedding Model for Your Use Case

For Speed-Critical Applications (Real-Time Chat, Search):

  • Model: all-MiniLM-L6-v2 (384 dimensions)
  • Why: Fast embedding generation (~10ms per query), good quality for most use cases
  • Example: Real-time customer support chatbots, live search systems

For Quality-Critical Applications (Legal, Medical):

  • Model: all-mpnet-base-v2 (768 dimensions) or OpenAI text-embedding-ada-002 (1536 dimensions)
  • Why: Higher quality embeddings, better semantic understanding
  • Example: Legal document analysis, medical information systems

For Multilingual Applications:

  • Model: multilingual-MiniLM-L6-v2 or paraphrase-multilingual-MiniLM-L12-v2
  • Why: Understands 50+ languages, enables cross-lingual search
  • Example: Global customer support, international content platforms

For Domain-Specific Applications:

  • Model: Domain-specific models (e.g., BioBERT for medical, Legal-BERT for legal)
  • Why: Trained on domain-specific text, better understanding of specialized terminology
  • Example: Medical research platforms, legal document systems

Test Your Understanding

Question 1: What are text embeddings?

A) Text compression algorithms
B) Text formatting methods
C) Dense vector representations of text that capture semantic meaning, allowing similar texts to have similar vectors
D) Text storage formats

Question 2: Interview question: "What is the difference between dense and sparse embeddings?"

A) Dense is faster
B) Dense embeddings are learned vector representations (e.g., 384-dim) that capture semantic meaning. Sparse embeddings are high-dimensional with mostly zeros, based on word frequencies (e.g., TF-IDF, BM25)
C) Sparse is better
D) They are the same

Question 3: What is cosine similarity used for in embeddings?

A) Training embedding models
B) Generating embeddings
C) Storing embeddings
D) Measuring semantic similarity between embedding vectors by computing the cosine of the angle between them (range: -1 to 1)

Question 4: What are popular embedding models used in RAG systems?

A) Only GPT models
B) Only BERT models
C) OpenAI text-embedding-ada-002, Sentence-BERT (all-MiniLM-L6-v2), and other transformer-based models
D) Only word2vec

Question 5: Interview question: "How do you choose the right embedding dimension?"

A) Always use highest dimension
B) Always use lowest dimension
C) Dimension doesn't matter
D) Balance between quality (higher dim = better) and efficiency (lower dim = faster, less storage). Common: 384-1536 dimensions. Consider model capabilities, storage costs, and retrieval speed requirements

Question 6: What does the cosine similarity formula \(\cos(\theta) = \frac{q \cdot d}{\|q\| \|d\|}\) represent?

A) Euclidean distance
B) The cosine of the angle between query vector q and document vector d, normalized by their magnitudes
C) Dot product
D) Vector addition

Question 7: Why do similar texts get similar embedding vectors?

A) They use the same words
B) They have the same length
C) Random chance
D) Embedding models are trained to map semantically similar texts to nearby points in vector space, capturing meaning rather than exact word matching

Question 8: Interview question: "How would you evaluate embedding quality?"

A) Only check embedding dimension
B) Only check model size
C) No evaluation needed
D) Use semantic similarity benchmarks (STS, SICK), test on domain-specific tasks, measure retrieval performance (precision@k, recall@k), and evaluate on downstream RAG tasks

Question 9: What is the advantage of using pre-trained embedding models?

A) They are always better than custom models
B) They capture general semantic knowledge from large text corpora, work well out-of-the-box, and don't require training on your specific data
C) They are faster to train
D) They use less memory

Question 10: What is the typical embedding dimension range used in production RAG systems?

A) 10-50 dimensions
B) 10000+ dimensions
C) Dimension doesn't matter
D) 384-1536 dimensions, with 384-768 being common for efficiency and 1536 for higher quality

Question 11: Interview question: "How do you handle out-of-vocabulary words in embeddings?"

A) Skip those words
B) Use random vectors
C) OOV words don't exist
D) Modern embedding models (subword tokenization) handle OOV words by breaking them into subwords. For truly unknown tokens, models use special UNK tokens or character-level embeddings

Question 12: What is the relationship between embedding quality and RAG performance?

A) Embedding quality doesn't affect RAG
B) Only LLM quality matters
C) Embeddings are optional
D) Better embeddings lead to more accurate semantic retrieval, which improves RAG answer quality. Embedding quality directly impacts retrieval precision and recall