← Back to Vector Database

Module 1: Vector Fundamentals

Distance geometry, storage layouts, and the daily drills for Days 1–6

01

Cognitive Load Analysis & Prerequisite Map

Mastering vector retrieval and production RAG requires moving from foundational linear algebra to storage engines, advanced orchestration abstractions, and distributed infrastructure.

                                      [Prerequisite Dependency Graph]

[Module 1: Vector Fundamentals & Distance Geometry]
   │  ├── Vector Spaces, L2 Norm, Dot Product, Cosine Geometry
   │  └── Storage Paradigms: Relational Tables vs. Flat Vectors vs. Embeddings
   ▼
[Module 2: Chroma DB Architecture, HNSW & Dual-Filtering]
   │  ├── HNSW Multi-Layer Skip-List Graph Traversal (M, ef_construction, ef_search)
   │  └── Storage Engine & Filtering Mechanics (Metadata 'where' + Document Full-Text 'where_document')
   ▼
[Module 3: Advanced Orchestration Retrievers (LangChain & LlamaIndex)]
   │  ├── Dense Semantic vs. Sparse Lexical Search (BM25: k1, b saturation)
   │  ├── Diversity & Context Expansion (MMR, Multi-Query, Parent Document / Auto-Merging)
   │  └── Rank Aggregation Algorithms (Reciprocal Rank Fusion - RRF)
   ▼
[Module 4: Enterprise Indexing, FAISS Scaling & Production Pipelines]
      ├── Quantization & Partitioning Trade-offs (Flat vs. IVF vs. LSH vs. HNSW)
      ├── 8-Stage Production RAG Pipeline & Hard Architectural Boundaries
      └── Production Failure Modes (Embedding Drift, Asymmetric Models, Sharded Migrations)

Cognitive Load & Time Budgeting Strategy (20–30 Minutes/Day)

  • Minutes 0–8 (Micro-Concept Mastery): Zero in on a single architectural invariant, index structure, or mathematical formula. Avoid sprawling background reading.
  • Minutes 8–28 (Targeted Drills): Complete 5 to 7 high-density coding or mathematical drills designed to build muscle memory (e.g., computing distance matrices manually, debugging query filter ASTs, or implementing indexing pipelines).
  • Minutes 28–30 (Self-Verification): Audit edge cases against the provided solution keys.

02

Module Roadmap & Milestone Overview

ModuleCore Architecture FocusWeekly Milestone Deliverable
Module 1Mathematical Foundations & Distance MetricsBuild an exact brute-force vector engine from scratch in NumPy with metric parity to Chroma/FAISS.
Module 2Chroma DB Internals, HNSW Tuning & Dual FilteringProduction-grade metadata-filtered vector catalog with graph parameter tuning and regression benchmarks.
Module 3Advanced Retrievers (LangChain & LlamaIndex)Hybrid RRF Retriever combining BM25 lexical scoring and dense vector embeddings with parent-child chunk resolution.
Module 4FAISS Quantization, Scaling & Production RAG8-stage enterprise-grade RAG pipeline featuring strict boundary isolation, query caching, and index failover.

DAYS

Daily schedule · Days 1 to 6

DAY 1

Vector Database vs. RDBMS Paradigms & Storage Layouts

Micro-Concept Mastery (8 Mins)

  • Relational Database Management Systems (RDBMS): Organize data into structured rows, columns, and relations. Query execution relies on deterministic relational algebra evaluated over structured keys (B-Tree/B+ Tree indexes) with worst-case scalar lookups of $\mathcal{O}(\log N)$.
  • Vector Databases: Store unstructured entity embeddings as dense, high-dimensional floating-point vectors ($D \in \mathbb{R}^d$). Search is non-deterministic: it evaluates semantic proximity in multi-dimensional vector space using metric geometry.
  • Storage & Indexing Invariant: While an RDBMS optimizes for exact matches ($=$) or range scans ($<, >$) across partitioned disk blocks, a vector database uses spatial proximity graphs or Voronoi cells to solve the Approximate Nearest Neighbor (ANN) problem across multi-dimensional topological manifolds.

Daily Drills (20 Mins)

Drill 1.1: Storage Architecture Trade-Offs

State the asymptotic time complexity of exact brute-force search over $N$ vectors of dimension $d$. Why does a standard B-Tree index fail to accelerate similarity queries in high-dimensional spaces ($d > 100$)?

  • Answer & Explanation: Exact search requires $\mathcal{O}(N \cdot d)$ floating-point operations because every stored vector must be compared to the query. B-Trees rely on total 1D ordering (scalars). Projecting multi-dimensional space onto 1D curves (e.g., Z-order, Hilbert curves) causes space-filling degradation (the "curse of dimensionality"), forcing index traversals to degrade into full linear scans $\mathcal{O}(N)$.

Drill 1.2: Identifying Use-Case Misalignments

A bank requires sub-millisecond retrieval of account balances by account_id with strict ACID guarantees. An engineer proposes Chroma DB. Why is this an architectural antipattern?

  • Answer & Explanation: Vector databases trade strict transactional ACID consistency and exact primary-key B-Tree lookups for approximate similarity searches over non-scalar embeddings. Account balances require deterministic, single-row lookups with transactional serializability—a core RDBMS/Key-Value workload.

Drill 1.3: Embeddings Representation in Memory

Given a collection of 500,000 document embeddings, each with dimension $d = 1536$ using single-precision floating point (float32), calculate the raw unindexed RAM footprint in megabytes (MB) or gigabytes (GB).

  • Step-by-Step Calculation:

$$\text{Memory} = N \times d \times \text{sizeof}(\text{float32}) = 500{,}000 \times 1536 \times 4\text{ bytes}$$

$$\text{Memory} = 3{,}072{,}000{,}000\text{ bytes} = \frac{3{,}072{,}000{,}000}{1024^3}\text{ GiB} \approx 2.861\text{ GiB}$$

  • Answer: $3.072\text{ GB}$ (or $\approx 2.861\text{ GiB}$).

Drill 1.4: In-Memory Vector Operations

Write a pure Python/NumPy function to simulate raw vector ingestion and compute the memory overhead of storing dynamic vectors versus a contiguous NumPy array.

  • Code & Explanation:
import sys
import numpy as np

def calculate_memory_footprint(n_vectors: int, dim: int):
    # Dynamic Python list of lists
    py_list = [[0.0] * dim for _ in range(n_vectors)]
    # Python overhead includes list pointer arrays + PyObject wrapping float values
    list_mem = sys.getsizeof(py_list) + sum(sys.getsizeof(row) for row in py_list)

    # Contiguous C-aligned buffer
    np_array = np.zeros((n_vectors, dim), dtype=np.float32)
    np_mem = np_array.nbytes + sys.getsizeof(np_array)

    return list_mem, np_mem

# Contiguous buffers prevent pointer chasing and maximize CPU cache-line hits during matrix-vector multiplies.

Drill 1.5: Schema Design Analysis

Contrast how document metadata is indexed in an RDBMS versus how it is bound to vectors in a vector database during vector ingestion.

  • Answer & Explanation: In an RDBMS, metadata is stored in structured tables where secondary B-Trees index specific columns directly. In vector databases, metadata is stored as JSON/Key-Value payloads paired with an internal vector ID. The vector DB builds inverted indexes or secondary posting lists mapped directly to the internal integer IDs of the vector graph or inverted list.

เปิดบทเรียนวันที่ 1 →

DAY 2

Vector Libraries (FAISS) vs. Persistent Vector DBs (Chroma DB, Milvus)

Micro-Concept Mastery (8 Mins)

  • Vector Libraries (e.g., FAISS, Annoy): In-memory algorithm implementations. They lack native persistence engines, network daemons, dynamic CRUD capabilities, and document-level metadata filtering. Updates require full index rebuilding or complex manual index management.
  • Persistent Vector Databases (e.g., Chroma DB, Milvus): Provide complete database management systems. They support full dynamic CRUD (Create, Read, Update, Delete), transactional WAL (Write-Ahead Logging), immutable disk-backed segment storage, metadata filtering engines, and client-server or distributed architectures.

Daily Drills (20 Mins)

Drill 2.1: Architectural Distinction

Explain the technical distinction between FAISS and Chroma DB when handling a request to update a single vector payload associated with document ID "doc_042".

  • Answer & Explanation: Chroma DB exposes an update() or upsert() API that updates the document payload and recomputes the graph connections dynamically. FAISS is an index library lacking primary-key document mappings; updating a single vector requires maintaining an external ID mapping layer, deleting the vector ID (if supported by that index type), or rebuilding the entire index segment.

Drill 2.2: Technology Selection Scenario

A team is deploying a real-time semantic search feature on an edge device (iOS/Android mobile app) with a static index of 10,000 items. Should they choose FAISS, Chroma DB, or Milvus?

  • Answer & Explanation: FAISS (or FAISS Mobile/C++ bindings) is the appropriate choice. Milvus requires distributed cloud infrastructure (etcd, Pulsar/Kafka, MinIO), and Chroma DB introduces client-server or heavier runtime dependencies. A static 10k index requires an embedded, lightweight in-memory library with zero daemon overhead.

Drill 2.3: Failure Mode Diagnosis

An engineer uses FAISS to index 5 million vectors. During production, new items are appended using index.add() every 10 seconds. After 48 hours, memory usage balloons and search latency degrades. What architectural mechanism is missing?

  • Answer & Explanation: FAISS index types like IndexFlatL2 simply append vectors to contiguous memory buffers without rebalancing or segmenting. Dynamic additions to unsegmented, non-rebalanced in-memory indexes cause fragmentation, prevent memory reclamation, and lack the background compaction/segment merging handled natively by full vector databases like Milvus or Chroma DB.

Drill 2.4: Code Construction – Standalone FAISS Index

Write a complete Python script initializing a FAISS Flat L2 index, adding 1,000 random vectors ($d=64$), and searching for the top 5 nearest neighbors.

  • Code & Explanation:
import numpy as np
import faiss

d = 64
n_samples = 1000
query_vectors = 1

# Generate synthetic data
np.random.seed(42)
data = np.random.random((n_samples, d)).astype('float32')
query = np.random.random((query_vectors, d)).astype('float32')

# Instantiate FAISS index
index = faiss.IndexFlatL2(d)
index.add(data)

# Execute search
k = 5
distances, indices = index.search(query, k)

assert indices.shape == (1, 5)
assert distances.shape == (1, 5)
print(f"Top neighbor index: {indices[0][0]} with L2 distance: {distances[0][0]:.4f}")

Drill 2.5: CRUD Lifecycle Verification

List the 4 core CRUD operations in Chroma DB, detailing what occurs under the hood when .delete(where={"status": "deprecated"}) is invoked.

  • Answer & Explanation:

1. add() / upsert()

2. get() / query()

3. update() / modify()

4. delete()

When .delete(where=...) runs, the metadata index matches the predicate, resolves matching vector IDs, marks the nodes/vectors as deleted in the collection manifest (tombstoning), and prunes the IDs from subsequent query candidate traversals.


เปิดบทเรียนวันที่ 2 →

DAY 3

Euclidean ($L2$) Distance & Squared $L2$ Metric Mechanics

Micro-Concept Mastery (8 Mins)

  • Euclidean ($L2$) Distance Formula:

$$L2(\mathbf{a}, \mathbf{b}) = \Vert{}\mathbf{a} - \mathbf{b}\Vert{}_2 = \sqrt{\sum_{i=1}^d (a_i - b_i)^2}$$

  • Squared $L2$ Distance ($L2^2$):

$$L2^2(\mathbf{a}, \mathbf{b}) = \sum_{i=1}^d (a_i - b_i)^2$$

  • Optimization Invariant: Because $f(x) = \sqrt{x}$ is a strictly monotonically increasing function for $x \ge 0$, ranking vectors by $L2$ yields identical results to ranking by $L2^2$. Vector engines omit the square root operation during inner loops to avoid expensive CPU/GPU floating-point square root operations.

Daily Drills (20 Mins)

Drill 3.1: Manual Calculation (2D Metric Space)

Calculate the exact $L2$ distance and squared $L2$ distance between vectors $\mathbf{a} = [3.0, 7.0]$ and $\mathbf{b} = [6.0, 3.0]$.

  • Step-by-Step Calculation:

$$\Delta_1 = (3.0 - 6.0) = -3.0 \implies (-3.0)^2 = 9.0$$

$$\Delta_2 = (7.0 - 3.0) = 4.0 \implies (4.0)^2 = 16.0$$

$$L2^2(\mathbf{a}, \mathbf{b}) = 9.0 + 16.0 = 25.0$$

$$L2(\mathbf{a}, \mathbf{b}) = \sqrt{25.0} = 5.0$$

  • Answer: $L2^2 = 25.0$, $L2 = 5.0$.

Drill 3.2: High-Dimensional Manual Trace

Compute the $L2$ distance between 4D vectors $\mathbf{u} = [1.0, 0.0, -2.0, 4.0]$ and $\mathbf{v} = [3.0, -2.0, 0.0, 2.0]$.

  • Step-by-Step Calculation:

$$\sum (u_i - v_i)^2 = (1 - 3)^2 + (0 - (-2))^2 + (-2 - 0)^2 + (4 - 2)^2$$

$$\sum (u_i - v_i)^2 = (-2)^2 + (2)^2 + (-2)^2 + (2)^2 = 4 + 4 + 4 + 4 = 16.0$$

$$L2(\mathbf{u}, \mathbf{v}) = \sqrt{16.0} = 4.0$$

  • Answer: $4.0$.

Drill 3.3: Mathematical Verification of Invariance

Prove why minimizing $L2(\mathbf{q}, \mathbf{x})$ over a candidate set $X$ is mathematically equivalent to minimizing $\sum_{i=1}^d (q_i - x_i)^2$.

  • Answer & Explanation: Let $g(z) = \sqrt{z}$. For all $z_1, z_2 \in [0, \infty)$, $z_1 < z_2 \iff g(z_1) < g(z_2)$ because the derivative $g'(z) = \frac{1}{2\sqrt{z}} > 0$ for all $z > 0$. Hence, the square root preserves ordinal ranking: $\operatorname{argmin}_{\mathbf{x} \in X} \Vert{}\mathbf{q} - \mathbf{x}\Vert{}_2 = \operatorname{argmin}_{\mathbf{x} \in X} \Vert{}\mathbf{q} - \mathbf{x}\Vert{}_2^2$.

Drill 3.4: Pure NumPy Vectorized Batch $L2$ Engine

Write an optimized Python function that computes the pairwise $L2$ distance matrix between a query matrix $\mathbf{Q} \in \mathbb{R}^{M \times d}$ and an index matrix $\mathbf{X} \in \mathbb{R}^{N \times d}$ using the binomial expansion: $\Vert{}\mathbf{q} - \mathbf{x}\Vert{}^2 = \Vert{}\mathbf{q}\Vert{}^2 + \Vert{}\mathbf{x}\Vert{}^2 - 2\mathbf{q}\mathbf{x}^T$.

  • Code & Explanation:
import numpy as np

def batch_l2_squared(Q: np.ndarray, X: np.ndarray) -> np.ndarray:
    # Q: (M, d), X: (N, d)
    # ||q - x||^2 = ||q||^2 + ||x||^2 - 2 * <q, x>
    q_sq = np.sum(Q**2, axis=1, keepdims=True)  # (M, 1)
    x_sq = np.sum(X**2, axis=1, keepdims=True)  # (N, 1)
    dists_sq = q_sq + x_sq.T - 2 * np.dot(Q, X.T)  # (M, N)
    # Clip numerical underflow artifacts below 0.0
    return np.maximum(dists_sq, 0.0)

Drill 3.5: Curse of Dimensionality Edge Case

Why does the ratio $\frac{D_{\max} - D_{\min}}{D_{\min}}$ approach 0 as $d \to \infty$ for independent and identically distributed (i.i.d.) vector dimensions under $L2$?

  • Answer & Explanation: By the Law of Large Numbers, the Euclidean distance between any two random vectors concentrates tightly around its expected value as $d$ increases. The variance relative to the mean vanishes, causing the distance to the nearest neighbor to approach the distance to the farthest neighbor, which degrades $L2$-based nearest neighbor searches in unconstrained high dimensions.

เปิดบทเรียนวันที่ 3 →

DAY 4

Dot Product (Inner Product) Mechanics & Asymmetric Search

Micro-Concept Mastery (8 Mins)

  • Dot Product (Inner Product) Definition:

$$\mathbf{a} \cdot \mathbf{b} = \langle \mathbf{a}, \mathbf{b} \rangle = \sum_{i=1}^d a_i b_i = \Vert{}\mathbf{a}\Vert{} \Vert{}\mathbf{b}\Vert{} \cos(\theta)$$

  • Metric Classification: Dot product is a similarity metric (larger values indicate greater proximity). To use it in distance-minimizing search algorithms, databases map it to an inverted metric:

$$\text{Distance}_{IP}(\mathbf{a}, \mathbf{b}) = -(\mathbf{a} \cdot \mathbf{b}) \quad \text{or} \quad 1 - (\mathbf{a} \cdot \mathbf{b})$$

  • Magnitude Sensitivity: The inner product is unbounded ($(-\infty, \infty)$) and directly amplifies vector norms. A document with an identical direction to the query but a higher magnitude achieves a higher dot product score.

Daily Drills (20 Mins)

Drill 4.1: Manual Dot Product Trace

Given query vector $\mathbf{q} = [0.5, -1.5, 2.0]$ and candidate vector $\mathbf{d} = [2.0, 4.0, 1.0]$, calculate $\mathbf{q} \cdot \mathbf{d}$ and the corresponding negative inner product distance.

  • Step-by-Step Calculation:

$$\mathbf{q} \cdot \mathbf{d} = (0.5)(2.0) + (-1.5)(4.0) + (2.0)(1.0)$$

$$\mathbf{q} \cdot \mathbf{d} = 1.0 - 6.0 + 2.0 = -3.0$$

$$\text{Distance}_{IP} = -(\mathbf{q} \cdot \mathbf{d}) = -(-3.0) = 3.0$$

  • Answer: Dot Product $= -3.0$, Distance $= 3.0$.

Drill 4.2: Magnitude Amplification Analysis

Suppose $\mathbf{q} = [1, 1]$. Vector $\mathbf{v}_1 = [2, 2]$ and $\mathbf{v}_2 = [0, 10]$.

1. Which vector has the closest angular alignment to $\mathbf{q}$? 2. Which vector yields the highest Dot Product?

  • Step-by-Step Calculation:

$$\theta(\mathbf{q}, \mathbf{v}_1) = 0^\circ \implies \cos(\theta) = 1.0$$

$$\cos(\theta(\mathbf{q}, \mathbf{v}_2)) = \frac{1(0) + 1(10)}{\sqrt{2} \cdot 10} = \frac{10}{10\sqrt{2}} = \frac{1}{\sqrt{2}} \approx 0.707$$

$$\mathbf{q} \cdot \mathbf{v}_1 = 1(2) + 1(2) = 4.0$$

$$\mathbf{q} \cdot \mathbf{v}_2 = 1(0) + 1(10) = 10.0$$

  • Answer: $\mathbf{v}_1$ is closest angularly, but $\mathbf{v}_2$ yields a higher Dot Product ($10.0 > 4.0$) due to its larger magnitude.

Drill 4.3: Chroma DB Configuration Syntax

Write the Python configuration snippet to initialize a Chroma DB collection explicitly configured for Inner Product search (ip).

  • Code & Explanation:
import chromadb

client = chromadb.Client()
collection = client.create_collection(
    name="ip_search_space",
    metadata={"description": "Inner product collection"},
    configuration={
        "hnsw": {
            "space": "ip"  # 'l2', 'ip', or 'cosine'
        }
    }
)

Drill 4.4: Recommender System Design Trade-Off

In e-commerce recommendation systems, why is raw Inner Product often preferred over length-normalized metrics like Cosine Similarity?

  • Answer & Explanation: In recommendation embeddings, the vector direction represents category affinity, while the vector magnitude encodes user engagement, item popularity, or catalog review confidence. Using the inner product allows the system to recommend items that are both semantically relevant and popular.

Drill 4.5: BLAS Level-2 Matrix-Vector Optimization

Given an index matrix $\mathbf{X} \in \mathbb{R}^{N \times d}$ and a single query vector $\mathbf{q} \in \mathbb{R}^{d}$, state the BLAS operation used to execute an exact Inner Product search, and specify its computational complexity.

  • Answer & Explanation: It executes a BLAS Level-2 GEMV (General Matrix-Vector Multiplication): $\mathbf{y} = \alpha \mathbf{X} \mathbf{q} + \beta \mathbf{y}$. Time complexity is $\mathcal{O}(N \cdot d)$ floating-point operations.

เปิดบทเรียนวันที่ 4 →

DAY 5

Cosine Similarity, Cosine Distance & Normalization Mathematics

Micro-Concept Mastery (8 Mins)

  • Cosine Similarity:

$$S_{cos}(\mathbf{a}, \mathbf{b}) = \frac{\mathbf{a} \cdot \mathbf{b}}{\Vert{}\mathbf{a}\Vert{}_2 \Vert{}\mathbf{b}\Vert{}_2} = \frac{\sum a_i b_i}{\sqrt{\sum a_i^2}\sqrt{\sum b_i^2}}$$

  • Cosine Distance:

$$D_{cos}(\mathbf{a}, \mathbf{b}) = 1 - S_{cos}(\mathbf{a}, \mathbf{b})$$

  • L2-Normalized Equivalence: If vectors are normalized to unit length ($\Vert{}\mathbf{a}\Vert{}_2 = 1, \Vert{}\mathbf{b}\Vert{}_2 = 1$):

$$\mathbf{a}_{\text{norm}} = \frac{\mathbf{a}}{\Vert{}\mathbf{a}\Vert{}_2} \implies S_{cos}(\mathbf{a}_{\text{norm}}, \mathbf{b}_{\text{norm}}) = \mathbf{a}_{\text{norm}} \cdot \mathbf{b}_{\text{norm}}$$

  • Mathematical Identity connecting Cosine Distance and $L2^2$:

$$\Vert{}\mathbf{a}_{\text{norm}} - \mathbf{b}_{\text{norm}}\Vert{}_2^2 = \Vert{}\mathbf{a}_{\text{norm}}\Vert{}^2 + \Vert{}\mathbf{b}_{\text{norm}}\Vert{}^2 - 2(\mathbf{a}_{\text{norm}} \cdot \mathbf{b}_{\text{norm}}) = 2 - 2 S_{cos}(\mathbf{a}, \mathbf{b}) = 2 D_{cos}(\mathbf{a}, \mathbf{b})$$

Therefore:

$$D_{cos}(\mathbf{a}, \mathbf{b}) = \frac{1}{2} \Vert{}\mathbf{a}_{\text{norm}} - \mathbf{b}_{\text{norm}}\Vert{}_2^2$$

Daily Drills (20 Mins)

Drill 5.1: Manual Normalization & Metric Calculation

Given $\mathbf{a} = [4.0, 3.0]$ and $\mathbf{b} = [0.0, 5.0]$:

1. Compute the $L2$ norms $\Vert{}\mathbf{a}\Vert{}_2$ and $\Vert{}\mathbf{b}\Vert{}_2$. 2. Compute $S_{cos}(\mathbf{a}, \mathbf{b})$. 3. Compute $D_{cos}(\mathbf{a}, \mathbf{b})$.

  • Step-by-Step Calculation:

$$\Vert{}\mathbf{a}\Vert{}_2 = \sqrt{4.0^2 + 3.0^2} = \sqrt{16 + 9} = 5.0$$

$$\Vert{}\mathbf{b}\Vert{}_2 = \sqrt{0.0^2 + 5.0^2} = 5.0$$

$$\mathbf{a} \cdot \mathbf{b} = (4.0)(0.0) + (3.0)(5.0) = 15.0$$

$$S_{cos}(\mathbf{a}, \mathbf{b}) = \frac{15.0}{5.0 \times 5.0} = \frac{15.0}{25.0} = 0.60$$

$$D_{cos}(\mathbf{a}, \mathbf{b}) = 1.0 - 0.60 = 0.40$$

  • Answer: $\Vert{}\mathbf{a}\Vert{} = 5.0, \Vert{}\mathbf{b}\Vert{} = 5.0, S_{cos} = 0.60, D_{cos} = 0.40$.

Drill 5.2: Verification of the Cosine-to-$L2$ Identity

Using normalized vectors $\mathbf{a}_{\text{norm}} = [0.8, 0.6]$ and $\mathbf{b}_{\text{norm}} = [0.0, 1.0]$: Calculate $\Vert{}\mathbf{a}_{\text{norm}} - \mathbf{b}_{\text{norm}}\Vert{}_2^2$ and verify that it equals $2 \times D_{cos}(\mathbf{a}, \mathbf{b})$.

  • Step-by-Step Calculation:

$$\Vert{}\mathbf{a}_{\text{norm}} - \mathbf{b}_{\text{norm}}\Vert{}_2^2 = (0.8 - 0.0)^2 + (0.6 - 1.0)^2 = (0.8)^2 + (-0.4)^2 = 0.64 + 0.16 = 0.80$$

From Drill 5.1, $D_{cos}(\mathbf{a}, \mathbf{b}) = 0.40$.

$$2 \times D_{cos} = 2 \times 0.40 = 0.80$$

  • Answer: Verified ($0.80 == 0.80$).

Drill 5.3: Production Bug Diagnosis – Distance Output Inversion

A production RAG service returns documents ordered by increasing scores: [0.12, 0.35, 0.82]. The developer flags that the least relevant document was placed first. What caused this bug?

  • Answer & Explanation: The vector index is returning Cosine Distance ($D_{cos} \in [0, 2]$), where values closer to $0.0$ denote higher similarity. The developer assumed the output represented Cosine Similarity ($S_{cos} \in [-1, 1]$) and sorted descending, inverting the result order.

Drill 5.4: Vectorized Normalization Implementation

Write a vectorized Python/NumPy function to unit-normalize an arbitrary 2D array of embeddings $\mathbf{X} \in \mathbb{R}^{N \times d}$ while avoiding division-by-zero errors for zero vectors.

  • Code & Explanation:
import numpy as np

def normalize_embeddings(X: np.ndarray, eps: float = 1e-12) -> np.ndarray:
    # Compute L2 norm along feature axis
    norms = np.linalg.norm(X, axis=1, keepdims=True)
    # Clip denominator to prevent division by zero
    norms = np.maximum(norms, eps)
    return X / norms

Drill 5.5: Document Length Invariance Scenario

Why is Cosine Distance preferred over Euclidean Distance for dense text retrieval tasks using unnormalized sentence embeddings of variable-length texts?

  • Answer & Explanation: Document length can scale embedding magnitudes depending on the pooling strategy (e.g., mean vs. sum pooling). Euclidean distance is sensitive to magnitude variations, misinterpreting length differences as semantic divergence. Cosine distance isolates angular orientation, measuring semantic alignment independently of length.

เปิดบทเรียนวันที่ 5 →

DAY 6

Distance Metric Selection Matrix & System Benchmarking

Micro-Concept Mastery (8 Mins)

  • Metric Selection Decision Matrix:
  • Cosine Distance: Unnormalized embeddings, NLP semantic similarity, length-invariant document matching.
  • Dot Product (Inner Product): Pre-normalized embeddings (highest computational throughput), recommendation models with magnitude-encoded popularity.
  • Euclidean ($L2$): Physical/spatial coordinates, computer vision embeddings, geometric clustering (k-means).
  • Computational Cost Invariant: On identical hardware, evaluating $\mathbf{a} \cdot \mathbf{b}$ requires $d$ multiplications and $d-1$ additions. Evaluating raw $L2$ requires $d$ subtractions, $d$ squarings, $d-1$ additions, and a square root operation.

Daily Drills (20 Mins)

Drill 6.1: Metric Selection under Constraints

You are using an embedding model that outputs unit-normalized vectors ($\Vert{}\mathbf{v}\Vert{} = 1.0$). Which metric configuration in Chroma DB minimizes latency while maintaining search accuracy: "l2", "cosine", or "ip"?

  • Answer & Explanation: "ip" (Inner Product). Because the vectors are already normalized, Inner Product ranking matches Cosine ranking, while avoiding the redundant normalization checks and distance conversions performed by "cosine".

Drill 6.2: FLOP Complexity Comparison

Calculate the exact number of floating-point operations (FLOPs) required to compute:

1. Dot Product between two vectors of dimension $d = 1024$. 2. Squared Euclidean Distance ($L2^2$) between two vectors of dimension $d = 1024$.

  • Step-by-Step Calculation:
  • Dot Product: 1024 multiplications $+$ 1023 additions $= 2047$ FLOPs.
  • $L2^2$: 1024 subtractions $+$ 1024 multiplications (squaring) $+$ 1023 additions $= 3071$ FLOPs.
  • Answer: Dot Product $= 2047$ FLOPs; $L2^2 = 3071$ FLOPs. Dot Product requires $\approx 33\%$ fewer operations.

Drill 6.3: Chroma DB Distance Function Bug

Review the following snippet:

import chromadb
client = chromadb.Client()
coll = client.create_collection("docs", configuration={"hnsw": {"space": "euclidean"}})

What runtime exception or operational error occurs?

  • Answer & Explanation: Chroma DB's underlying HNSW engine accepts only "l2", "ip", or "cosine" for its space parameter. Passing "euclidean" raises a validation ValueError. The correct identifier for Euclidean distance is "l2".

Drill 6.4: Full Brute-Force Metric Engine Implementation

Implement a Python class BruteForceVectorEngine supporting dynamic ingestion of vectors and exact top-$k$ search over "l2", "ip", and "cosine".

  • Code & Explanation:
import numpy as np

class BruteForceVectorEngine:
    def __init__(self, metric: str = "cosine"):
        assert metric in ["l2", "ip", "cosine"]
        self.metric = metric
        self.vectors = None
        self.ids = []

    def add(self, ids: list[str], vectors: np.ndarray):
        if self.vectors is None:
            self.vectors = vectors.astype(np.float32)
        else:
            self.vectors = np.vstack([self.vectors, vectors.astype(np.float32)])
        self.ids.extend(ids)

    def search(self, query: np.ndarray, k: int = 5):
        q = query.astype(np.float32).flatten()
        if self.metric == "l2":
            dists = np.sum((self.vectors - q) ** 2, axis=1)
            idx = np.argsort(dists)[:k]
            return [(self.ids[i], float(dists[i])) for i in idx]
        elif self.metric == "ip":
            scores = np.dot(self.vectors, q)
            idx = np.argsort(scores)[::-1][:k]  # Maximize
            return [(self.ids[i], float(scores[i])) for i in idx]
        elif self.metric == "cosine":
            norm_q = q / (np.linalg.norm(q) + 1e-12)
            norm_v = self.vectors / (np.linalg.norm(self.vectors, axis=1, keepdims=True) + 1e-12)
            sims = np.dot(norm_v, norm_q)
            dists = 1.0 - sims
            idx = np.argsort(dists)[:k]  # Minimize distance
            return [(self.ids[i], float(dists[i])) for i in idx]

Drill 6.5: Performance Audit of Asymmetric Models

An engineer uses a dual-encoder model where query embeddings have an average norm $\Vert{}\mathbf{q}\Vert{} \approx 1.0$, while document embeddings have norms proportional to document length ($\Vert{}\mathbf{d}\Vert{} \in [2.0, 15.0]$). What happens if they switch from Cosine to Dot Product search?

  • Answer & Explanation: Long documents will dominate the top-$k$ results regardless of semantic relevance because their higher norms scale the dot product scores upward. Cosine normalization is required to evaluate true semantic alignment independently of document length.

03

Weekly Review Quiz: Module 1 (Day 7)

Question 1: Index Type Asymptotics

Which of the following correctly describes the time complexity of a brute-force search over $N$ vectors of dimension $d$, and why can it not scale to enterprise workloads of $N = 10^8$?

  • A) $\mathcal{O}(\log N)$, bounded by memory bandwidth.
  • B) $\mathcal{O}(N \cdot d)$, requiring over $10^{10}$ FLOPs per query.
  • C) $\mathcal{O}(N^2 \cdot d)$, because of pairwise index matrix comparisons.
  • D) $\mathcal{O}(d \log N)$, due to tree traversal limits.
  • Answer: B.
  • Explanation: Brute-force search computes distance against every stored vector. For $10^8$ vectors at $d=768$, a single query requires $10^8 \times 768 \times 2 \approx 1.53 \times 10^{11}$ FLOPs, resulting in excessive query latency without ANN indexing.

Question 2: Memory Footprint Calculation

You are indexing $2{,}000{,}000$ dense vectors with dimension $d = 768$ using float32. What is the raw memory consumed by these embeddings (excluding graph/index overhead)?

  • A) $1.536\text{ GB}$
  • B) $3.072\text{ GB}$
  • C) $6.144\text{ GB}$
  • D) $12.288\text{ GB}$
  • Answer: C.
  • Explanation: $2 \times 10^6 \times 768 \times 4\text{ bytes} = 6{,}144{,}000{,}000\text{ bytes} \approx 6.144\text{ GB}$.

Question 3: Metric Mathematics ($L2$ vs. Cosine)

Given vectors $\mathbf{u} = [0, 3]$ and $\mathbf{v} = [0, 9]$, what are their Cosine Distance and $L2$ Distance?

  • A) $D_{cos} = 0.0$, $L2 = 6.0$
  • B) $D_{cos} = 1.0$, $L2 = 6.0$
  • C) $D_{cos} = 0.0$, $L2 = 36.0$
  • D) $D_{cos} = 0.5$, $L2 = 6.0$
  • Answer: A.
  • Explanation: Both vectors point along the positive y-axis ($\theta = 0^\circ$), so $\cos(\theta) = 1.0 \implies D_{cos} = 1 - 1 = 0.0$. The Euclidean distance is $\vert{}3 - 9\vert{} = 6.0$.

Question 4: Metric Invariance Proof

Under what condition does Inner Product distance ranking match Squared Euclidean Distance ($L2^2$) ranking?

  • A) When all vectors have negative values.
  • B) When all vectors in the dataset and query are normalized to unit $L2$ norm.
  • C) When dimension $d < 10$.
  • D) When the vectors are orthogonal.
  • Answer: B.
  • Explanation: Expanding the squared Euclidean distance yields $\Vert{}\mathbf{q} - \mathbf{x}\Vert{}^2 = \Vert{}\mathbf{q}\Vert{}^2 + \Vert{}\mathbf{x}\Vert{}^2 - 2\langle \mathbf{q}, \mathbf{x} \rangle$. When $\Vert{}\mathbf{q}\Vert{} = 1$ and $\Vert{}\mathbf{x}\Vert{} = 1$, this simplifies to $\Vert{}\mathbf{q} - \mathbf{x}\Vert{}^2 = 2 - 2\langle \mathbf{q}, \mathbf{x} \rangle$. Minimizing $L2^2$ directly maximizes the inner product $\langle \mathbf{q}, \mathbf{x} \rangle$.

Question 5: Code Debugging – Metric Space Configuration

Identify the bug in the following Chroma DB collection setup:

import chromadb
client = chromadb.PersistentClient(path="./chroma_store")
coll = client.create_collection(
    name="prod_vdb",
    metadata={"hnsw:space": "cosine_similarity"}
)
  • A) PersistentClient does not take a path parameter.
  • B) The space parameter must be configured under configuration={"hnsw": {"space": "cosine"}}.
  • C) Cosine distance cannot be used with persistent clients.
  • D) The collection name cannot contain underscores.
  • Answer: B.
  • Explanation: Chroma DB sets its distance metric via the collection configuration dictionary using the key "space", which accepts "cosine", "l2", or "ip". Specifying "cosine_similarity" in metadata is ignored or triggers a validation error.

Question 6: Relational vs. Vector Database Capabilities

Which of the following operations cannot be natively performed by an in-memory vector library like FAISS without external application-level code?

  • A) Batch matrix similarity computation.
  • B) k-nearest neighbor index search.
  • C) Direct metadata filtering: WHERE author = 'Alice' AND year >= 2023.
  • D) Index serialization to a local file.
  • Answer: C.
  • Explanation: FAISS indexes only raw vectors and integer offsets; it has no native metadata store or SQL/predicate filtering engine.

Question 7: Manual Math Drill – Cosine Similarity

Calculate the cosine similarity between $\mathbf{x} = [1, 2, 3, 4]$ and $\mathbf{y} = [2, 0, 0, 2]$.

  • A) $0.456$
  • B) $0.645$
  • C) $0.707$
  • D) $0.816$
  • Answer: B.
  • Explanation:

$$\mathbf{x} \cdot \mathbf{y} = 1(2) + 2(0) + 3(0) + 4(2) = 2 + 8 = 10$$

$$\Vert{}\mathbf{x}\Vert{} = \sqrt{1^2 + 2^2 + 3^2 + 4^2} = \sqrt{1 + 4 + 9 + 16} = \sqrt{30} \approx 5.477$$

$$\Vert{}\mathbf{y}\Vert{} = \sqrt{2^2 + 0^2 + 0^2 + 2^2} = \sqrt{4 + 4} = \sqrt{8} \approx 2.828$$

$$S_{cos} = \frac{10}{5.477 \times 2.828} = \frac{10}{15.491} \approx 0.6455$$

Question 8: Chroma DB Persistence Invariant

What occurs if a developer modifies an existing Chroma DB collection's distance metric from "l2" to "cosine" using collection.modify()?

  • A) The database automatically rebuilds the HNSW graph in the background.
  • B) An exception is raised: distance metrics and embedding functions are immutable after collection creation and require cloning.
  • C) Existing vectors retain L2, while newly inserted vectors use Cosine.
  • D) Chroma DB converts the vectors to unit length without rebuilding the index.
  • Answer: B.
  • Explanation: The distance metric defines the geometry of the underlying HNSW graph. Modifying it on an active collection is disallowed; applying a new metric requires creating a new collection and re-inserting the data.

Question 9: Algorithmic Mechanics – Monotonicity

Why do production vector indexes compute Squared Euclidean Distance ($L2^2$) rather than Euclidean Distance ($L2$) during inner-product distance comparisons?

  • A) $L2^2$ prevents floating-point underflow.
  • B) The square root is computationally expensive and does not change the ordinal ranking of neighbors.
  • C) $L2^2$ works on non-Euclidean manifolds, while $L2$ does not.
  • D) FAISS cannot store fractional floating-point values.
  • Answer: B.
  • Explanation: Avoiding the square root operation saves CPU/GPU cycles per distance evaluation across millions of vectors while preserving the exact rank ordering.

Question 10: Vector DB CRUD Semantics

When calling collection.update(ids=["id1"], documents=["New text"]) in Chroma DB, what happens to the underlying vector embeddings?

  • A) The document text updates, but the old embedding persists.
  • B) The call fails unless an explicit embedding vector is provided.
  • C) Chroma DB uses the collection's registered embedding function to recompute the vector in the background.
  • D) The document is appended as a new version with ID "id1_v2".
  • Answer: C.
  • Explanation: Chroma DB automatically calls the collection's configured embedding function to generate a new vector and updates the index accordingly.

Question 11: Edge-Case Scenario – Zero Vector Evaluation

What happens if you compute the Cosine Similarity between vector $\mathbf{a} = [0, 0, 0]$ and vector $\mathbf{b} = [1, 2, 3]$?

  • A) Similarity is $0.0$.
  • B) Similarity is $1.0$.
  • C) A division-by-zero occurs ($\Vert{}\mathbf{a}\Vert{}_2 = 0$), producing NaN or an arithmetic exception unless handled with an epsilon offset.
  • D) The system projects the zero vector to $[1, 1, 1]$.
  • Answer: C.
  • Explanation:

$$S_{cos} = \frac{\mathbf{a} \cdot \mathbf{b}}{\Vert{}\mathbf{a}\Vert{} \Vert{}\mathbf{b}\Vert{}} = \frac{0}{0 \cdot \sqrt{14}} = \frac{0}{0}$$

Vector engines must guard against this using a small epsilon ($\epsilon \approx 10^{-12}$) in the denominator.

Question 12: Production Hardware Sizing

You are designing an in-memory vector search system for $10{,}000{,}000$ vectors ($d = 1536$, float32). What is the minimum RAM required for the raw vectors alone, and what sizing guideline should be applied for an HNSW index?

  • A) $15.36\text{ GB}$; budget $16\text{ GB}$.
  • B) $30.72\text{ GB}$; budget $35\text{ GB}$.
  • C) $61.44\text{ GB}$; budget $90\text{–}120\text{ GB}$ to accommodate HNSW graph overhead.
  • D) $120\text{ GB}$; budget $500\text{ GB}$.
  • Answer: C.
  • Explanation: Raw vector storage is $10^7 \times 1536 \times 4\text{ bytes} \approx 61.44\text{ GB}$. An HNSW index adds graph edges ($M$ links per node across layers), which typically increases memory usage by 1.5× to 2×.

04

Module 1 Graduation Exam Specification

Project: Production-Grade Vector Evaluation Engine (VectorArch-Core)

System Architecture Overview

Build a standalone, mathematically validated, high-throughput vector processing engine in pure Python/NumPy without high-level vector database libraries. The engine must mirror the lower-level metric mechanics of systems like Chroma DB and FAISS.

                     +---------------------------------------+
                     |       VectorArch-Core Engine          |
                     +---------------------------------------+
                                         |
            +----------------------------+---------------------------+
            |                            |                           |
            v                            v                           v
   [Ingestion Engine]           [Storage Segment]           [Metric Dispatcher]
   - ID Mapping Table           - Contiguous Array          - L2 Squared
   - Metadata Store               (C-contiguous float32)     - Inner Product
   - Dynamic Resizing           - Tombstone Bitmask         - Cosine (w/ Epsilon)

Core Deliverables & Technical Requirements

Deliverable 1: Core Engine Implementation (engine.py)

  • Construct a class VectorStorageEngine(dim: int, metric: str = "cosine"):
  • Pre-allocate memory using contiguous NumPy arrays (np.float32) with dynamic resizing.
  • Implement CRUD operations:
  • insert(id: str, vector: np.ndarray, metadata: dict)
  • update(id: str, vector: np.ndarray, metadata: dict)
  • delete(id: str): Use tombstone marking to avoid expensive dynamic array reshaping.
  • vacuum(): Compact memory by purging tombstoned vectors.
  • Implement search(query: np.ndarray, k: int, filter_fn: callable = None):
  • Compute distances using the configured metric: "l2", "ip", or "cosine".
  • Apply optional metadata filtering.
  • Return results as a list of tuples: [(id, distance, metadata), ...].

Deliverable 2: Mathematical Parity Test Suite (test_parity.py)

  • Implement automated unit tests comparing your engine's output against scipy.spatial.distance or pure linear algebra across:
  • 1,000 synthetic vectors ($d = 128$).
  • Edge cases: Colinear vectors, orthogonal vectors, zero vectors, and inverted vectors.
  • Floating-point precision matching within a tolerance of $\text{atol} \le 10^{-6}$.

Deliverable 3: Profiling & Benchmark Report (report.md)

  • Benchmark search throughput (Queries Per Second - QPS) across varying dataset sizes: $N \in \{1000, 10000, 50000\}$.
  • Analyze latency differences between "l2", "cosine", and "ip".

Evaluation Rubric (100-Point Scale)

CriterionTarget RequirementPoints
Mathematical ParityOutputs for $L2$, Cosine, and Dot Product match reference implementations to within $10^{-6}$ precision across all edge cases (including zero vectors).25
Storage ArchitectureVectors are stored in contiguous, C-aligned float32 arrays. Deletion uses tombstone bitmasks; dynamic resizing amortizes allocation overhead.25
Search Engine CorrectnessQuery execution accurately handles top-$k$ ranking, distance metric sorting (minimizing for $L2$/Cosine, maximizing for IP), and metadata filtering predicates.20
Memory ManagementCompaction (vacuum()) properly reclaims tombstoned slots and updates internal index maps without data corruption.15
Engineering RigorClean code structure with strict typing, detailed docstrings, zero unhandled division-by-zero exceptions, and automated benchmarks.15

Passing this exam requires a score of 85/100 or higher to graduate to Module 2.