Cybersecurity

What Is a Vector?

Python Machine Learning Deep Learning LLMs RAG MCP AI Agents Cybersecurity Databases NumPy Scikit-learn Data Science Data Analysis Statistics Transformers Embeddings Vector Search Semantic Search
1,643 words Includes Code
Key Takeaway
You do not need a GPU, a vector database, or a heavy AI framework to understand and build vector search. A small private vector store can be built using pure Python, basic mathematics, and simple data structures — giving you a strong foundation for understanding how semantic search actually works.
Building a private vector store in pure Python with local embeddings and cosine similarity

Every search engine faces the same fundamental challenge: given a user's query, find the most relevant content. Traditional keyword search matches words. Semantic search attempts to understand meaning.

A vector database is useful at scale, but the underlying mathematics is surprisingly simple. In this tutorial, you will build a working local vector store using nothing but Python's standard library.

What Is a Vector?

In data science, a vector is simply a list of numbers that represents something:

# A 5-dimensional vector
vector = [0.21, 0.74, -0.13, 0.56, 0.89]

Each number represents a dimension. In practice, embedding vectors often have hundreds or thousands of dimensions, but the concept is the same.

Do not assume that arbitrary numbers automatically have semantic meaning. Vectors become meaningful when they are produced by an embedding model trained to capture relationships between pieces of information.

What Is an Embedding?

An embedding is a numerical representation of text (or images, audio, or other data) that captures semantic relationships. An embedding model converts text into a vector:

# Conceptual
# "Python is a programming language"
# → [0.12, -0.34, 0.87, ..., 0.45]

# "Machine learning builds models from data"
# → [0.15, -0.28, 0.82, ..., 0.51]

# Semantically related texts produce vectors that
# are closer together in the embedding space.

The important distinction: embedding generation (turning text into vectors) usually requires a pretrained model. Vector storage and search (finding the most similar vectors) can be implemented with simple Python mathematics.

Do We Need Deep Learning?

Important distinction: No deep-learning framework is required to build the vector search engine itself. However, a useful semantic search system generally needs an embedding model to convert text into meaningful vectors. This article builds the search engine — the part you can understand completely.

The Mathematics of Vector Search

Three common ways to compare vectors:

Method Formula Intuition
Cosine Similarity(A·B) / (‖A‖×‖B‖)Angle between vectors
Euclidean Distance√Σ(Aᵢ−Bᵢ)²Straight-line distance
Dot ProductΣ(Aᵢ × Bᵢ)Magnitude-weighted similarity
Cosine similarity showing the angle between two vectors determines their similarity score

We focus on cosine similarity because it measures the direction of vectors rather than their magnitude — which makes it ideal for comparing text embeddings where semantic direction matters more than absolute values.

A concrete example:

A = [1, 0]
B = [0.9, 0.1]
C = [0, 1]

cosine(A, B) = 0.9939  ← very similar
cosine(A, C) = 0.0000  ← completely different

Build a Vector Store From Scratch

The complete implementation uses only Python's standard library — no external dependencies:

import json, math
from typing import List, Dict, Any, Optional


def cosine_similarity(vec_a: List[float], vec_b: List[float]) -> float:
    """Compute cosine similarity between two vectors."""
    if len(vec_a) != len(vec_b):
        raise ValueError(f"Dimension mismatch: {len(vec_a)} vs {len(vec_b)}")

    dot = sum(a * b for a, b in zip(vec_a, vec_b))
    mag_a = math.sqrt(sum(a * a for a in vec_a))
    mag_b = math.sqrt(sum(b * b for b in vec_b))

    if mag_a == 0 or mag_b == 0:
        return 0.0
    return dot / (mag_a * mag_b)


class LocalVectorStore:
    """A simple local vector store."""

    def __init__(self, dimension: Optional[int] = None):
        self._dimension = dimension
        self._store: Dict[str, Dict[str, Any]] = {}

    def add(self, doc_id, text, vector, metadata=None):
        if self._dimension is None:
            self._dimension = len(vector)
        elif len(vector) != self._dimension:
            raise ValueError(f"Expected {self._dimension}, got {len(vector)}")
        self._store[doc_id] = {
            "id": doc_id, "text": text,
            "vector": vector, "metadata": metadata or {}
        }

    def search(self, query_vector, top_k=5):
        if not self._store:
            return []
        results = []
        for doc in self._store.values():
            score = cosine_similarity(query_vector, doc["vector"])
            results.append({"id": doc["id"], "text": doc["text"],
                           "score": round(score, 4), "metadata": doc["metadata"]})
        results.sort(key=lambda x: x["score"], reverse=True)
        return results[:top_k]

    def delete(self, doc_id):
        if doc_id in self._store:
            del self._store[doc_id]
            return True
        return False

    def count(self):
        return len(self._store)

    def save(self, filepath):
        with open(filepath, 'w') as f:
            json.dump({"dimension": self._dimension,
                       "documents": self._store}, f, indent=2)

    @classmethod
    def load(cls, filepath):
        with open(filepath, 'r') as f:
            data = json.load(f)
        store = cls(dimension=data["dimension"])
        store._store = data["documents"]
        return store

That is the complete implementation. Let us walk through it step by step.

How It Works: Step by Step

The search pipeline follows a clear sequence:

Vector search architecture showing indexing and querying paths through embedding model, vector store, and similarity search
  1. Add documents — each document gets an ID, text, vector, and optional metadata
  2. Store vectors — stored in a Python dictionary keyed by document ID
  3. Query — a query vector is compared against all stored vectors
  4. Score — cosine similarity produces a score between 0 and 1
  5. Rank — results are sorted by score descending
  6. Return top-k — the top-k most similar documents are returned

Try It: A Complete Example

# Create a store with 5-dimensional vectors
store = LocalVectorStore(dimension=5)

# Add documents with synthetic vectors
store.add("doc-001", "Python is a programming language.",
          [0.8, 0.2, 0.1, 0.0, 0.3])
store.add("doc-002", "Machine learning builds models from data.",
          [0.3, 0.9, 0.1, 0.0, 0.2])
store.add("doc-003", "Cybersecurity protects networks and systems.",
          [0.1, 0.0, 0.9, 0.7, 0.1])
store.add("doc-004", "Databases store structured data.",
          [0.2, 0.1, 0.0, 0.8, 0.6])
store.add("doc-005", "Statistics analyzes numerical data.",
          [0.4, 0.5, 0.0, 0.3, 0.4])

# Search with a programming-related query
results = store.search([0.7, 0.3, 0.1, 0.0, 0.2], top_k=3)

for r in results:
    print(f"{r['id']}: score={r['score']} — {r['text'][:40]}")

# Output:
# doc-001: score=0.9843 — Python is a programming language.
# doc-005: score=0.7909 — Statistics analyzes numerical data.
# doc-002: score=0.6851 — Machine learning builds models from d...

The Python-related document ranks highest because its vector is most similar to the query vector. The cosine similarity score tells you how similar — 0.9843 is very close to 1.0.

Adding Real Embeddings

The vectors in the example above were manually defined for clarity. In a real system, you connect the store to an embedding model:

# Conceptual: real embedding pipeline
from sentence_transformers import SentenceTransformer

model = SentenceTransformer("all-MiniLM-L6-v2")  # 384-dim, CPU-friendly

text = "Python is a programming language."
vector = model.encode(text).tolist()  # → list of 384 floats

store.add("doc-001", text, vector, metadata={"category": "programming"})

The key point: embedding generation is the computationally heavier step (it requires a model). Vector similarity search itself is comparatively simple — it is just math over lists of numbers.

For local CPU use, models like all-MiniLM-L6-v2 (384 dimensions) or all-mpnet-base-v2 (768 dimensions) are practical choices that run without a GPU.

Private Local Vector Search

When both embedding generation and vector search run locally, your data never leaves your machine:

# Local architecture
Local Documents
    ↓
Local Embedding Model (CPU)
    ↓
Local Vector Store (Python)
    ↓
Local Query
    ↓
Similarity Search
    ↓
Relevant Documents — never left your machine
Privacy note: "Local" does not automatically mean "secure." You must also secure your files, model, application, operating system, backups, and logs.

Add Metadata and Persistence

Metadata lets you filter and organize documents. The store supports saving to and loading from JSON:

# Save to disk
store.save("my_vector_store.json")

# Load later
loaded = LocalVectorStore.load("my_vector_store.json")
results = loaded.search(query_vector, top_k=3)

JSON persistence is appropriate for teaching and small collections (hundreds to thousands of vectors). For millions of vectors, production databases use binary formats and specialized indexes.

Performance Characteristics

For N vectors of dimension D, brute-force cosine similarity search is approximately O(N × D).

Dataset Size Dimensions Pure Python Practical?
100 documents384MillisecondsExcellent
1,000 documents384~10msExcellent
10,000 documents384~100msGood
1,000,000 documents384SecondsUse a vector DB

At scale, specialized indexes like HNSW (Hierarchical Navigable Small World) provide approximate nearest neighbor search that trades perfect accuracy for dramatic speed improvements.

Pure Python vs. NumPy vs. Vector Database

RAG pipeline showing documents flowing through vector store to retrieval and LLM answer generation
Requirement Pure Python NumPy Vector DB
LearningExcellentGoodMore abstraction
Small datasetExcellentExcellentOften unnecessary
PrototypeExcellentGoodUseful
Millions of vectorsPoor fitBetterAppropriate
Advanced indexingLimitedBasicStrong (HNSW, IVF)
ProductionDependsDependsUsually preferable

Building Toward RAG

The vector store is one component of a Retrieval-Augmented Generation (RAG) system. Here is how it fits:

Documents → Chunk → Embed → Vector Store

User Query → Embed → Similarity Search → Top-K Results

                        Retrieved Context + User Query

                               LLM generates answer

The vector store retrieves relevant context. The LLM uses that context to generate an answer. The store itself does not generate anything — it finds what is most relevant.

Limitations

  • Embedding quality matters — poor embeddings produce poor search results regardless of how good the vector store is
  • Chunking affects retrieval — how you split documents into chunks directly impacts what the system can find
  • Cosine similarity is not understanding — it measures vector similarity, not semantic meaning
  • Brute-force search does not scale indefinitely — O(N × D) becomes slow at millions of vectors
  • Vector similarity can produce false positives — mathematically similar vectors are not always semantically relevant
  • Multilingual performance varies — embedding models have different strengths across languages

Suggested Project

Build a local searchable knowledge base for your own notes:

private-vector-store/
├── documents/          # Your .md or .txt files
├── vector_store.json   # Persisted vectors
├── index.py            # Build the store
├── search.py           # Search the store
└── README.md

Suggested extensions: add metadata filtering, implement batch indexing, add a simple web interface, or connect to a local embedding model.

Key Takeaways

  • Vector search is simple math — cosine similarity, dot product, and Euclidean distance are the foundations
  • You can build a working vector store in ~50 lines of Python — no frameworks required
  • Embedding generation and vector search are separate concerns — the search engine does not need deep learning
  • Brute-force search works well for small to medium datasets — thousands of documents are perfectly manageable
  • Pure Python is for learning — production systems should use specialized vector databases
  • The vector store is one piece of RAG — retrieval, not generation

Related BestWordz Resources

Further Reading

Continue Learning: Data Science Pipeline

From data to insights

  1. The 10-Stage Data Science Roadmap
  2. What Is a Vector? (this article)
  3. What Is Prompt Engineering?
  4. Data Leakage in Machine Learning: 10 Mistakes That Destroy Your Model
  5. Keyword Search vs Semantic Search