1. Home
  2. Blog
  3. Artificial Intelligence

Artificial Intelligence

What Are Embeddings? Vectors and Cosine Similarity Explained Simply

Embeddings let computers compare meaning instead of matching exact words. They power semantic search, recommendations and RAG. Learn what they are, how similarity is measured, and where they let you down.

8 min read
What are embeddings? Vectors and cosine similarity explained simply

Key takeaways

  • An embedding is a list of numbers that represents the meaning of text, an image or other data.
  • Similar items end up with similar numbers, so meaning can be compared with math.
  • Cosine similarity compares direction, and it is a common way to score how alike two embeddings are.
  • Only compare embeddings from the same model, and test results on your own data.

Search for “how to get my money back”, and a good system should find a page titled “Refund policy”, even though the two share no important words. That ability comes from embeddings, one of the most useful ideas in modern AI.

This guide explains embeddings without heavy maths. We ran a small example in Python 3.13, and its output is shown below. The vectors in it are ones we made up by hand, with only three numbers each, to make the idea easy to see. Real embeddings come from a trained model and often have hundreds or thousands of numbers.

What is an embedding?

An embedding is a representation of something as a list of numbers, called a vector. A model is trained so that items with related meanings get vectors that are close together.

"kitten"  -> [0.9, 0.8, 0.1]
"cat"     -> [0.9, 0.7, 0.2]
"invoice" -> [0.0, 0.1, 0.9]

Once text is in this form, a computer can calculate how similar two pieces of text are, which it cannot do directly with words. Embeddings are not limited to words, either. Sentences, whole documents, images, audio and products can all be embedded.

Why not just match keywords?

Query and document Keyword search Embedding search
“money back” and “refund policy” Misses it, no shared words Can find it
“laptop” and “notebook computer” Misses it Can find it
“error E1042” and a page with “E1042” Finds it exactly May miss it
“bank” (river) and “bank” (money) Treats them alike Can separate them, given context

Our guide to RAG shows this in practice. A simple word-count search scored zero for “When can I get my money back?” against a refunds document, since they shared no words. Note the third row as well: exact identifiers are a weakness of embeddings, so keyword search still has a place.

Think of a map

Imagine placing every word on a huge map, where location reflects meaning. “Cat” and “kitten” sit almost next to each other, “dog” is in the same neighbourhood, and “invoice” is in a different city. To find related words, you simply look nearby.

A real embedding works the same way, except that the map has hundreds of dimensions rather than two. People cannot picture that, but the math works just the same. Individual numbers generally have no neat meaning, like “furriness”. What matters is the overall position.

A small working example

Here is how to compare our hand-made vectors:

import math

def cosine(a, b):
    dot = sum(x * y for x, y in zip(a, b))
    return dot / (math.sqrt(sum(x * x for x in a)) *
                  math.sqrt(sum(y * y for y in b)))

vectors = {
    "kitten":  [0.9, 0.8, 0.1],
    "cat":     [0.9, 0.7, 0.2],
    "dog":     [0.8, 0.3, 0.2],
    "invoice": [0.0, 0.1, 0.9],
}

for word in ("kitten", "dog", "invoice"):
    print(word, round(cosine(vectors["cat"], vectors[word]), 3))

# kitten 0.994
# dog 0.955
# invoice 0.238

Against “cat”, “kitten” scores highest, “dog” next, and “invoice” far lower. We chose these vectors to produce that pattern, so the ranking proves nothing about real language. It only shows how the calculation behaves. A semantic search does the same thing in three steps: embed the query, score it against every stored item, and sort.

query = [0.85, 0.75, 0.15]
sorted(vectors, key=lambda w: cosine(query, vectors[w]), reverse=True)

# ['kitten', 'cat', 'dog', 'invoice']

Cosine similarity explained

Each vector can be thought of as an arrow pointing from the origin. Cosine similarity measures the angle between two arrows, ignoring how long they are. We confirmed the key values:

cosine([1, 2, 3], [2, 4, 6])   # 1.0   same direction
cosine([1, 0], [0, 1])         # 0.0   unrelated (at right angles)
cosine([1, 0], [-1, 0])        # -1.0  opposite directions
Score Meaning
Close to 1 Point in nearly the same direction, so very similar
Close to 0 Unrelated
Close to -1 Point in opposite directions

Look at the first line. [2, 4, 6] is twice as long as [1, 2, 3], yet the score is a perfect 1.0, because direction is all that counts. That is useful for text, where length can reflect things like document size rather than meaning. Be careful with thresholds, too. There is no universal score that means “similar”, as useful ranges differ from model to model, so test on your own data.

Other distance measures

Measure What it looks at Notes
Cosine similarity Angle only A common default for text
Dot product Angle and length Fast, and equals cosine on normalized vectors
Euclidean distance Straight-line distance Smaller means more similar

The difference is easy to see. Those same two vectors with a cosine score of 1.0 have a Euclidean distance of 3.742, since one is longer than the other. Many systems normalize vectors, scaling each to a length of 1. After that, the dot product and cosine give the same answer, and we confirmed this: both gave 0.955 for “cat” and “dog”. Check your model’s documentation to see which measure it recommends.

How embeddings are learned

Nobody assigns these numbers by hand in real systems. A neural network learns them during training. An early and influential idea is that words appearing in similar contexts tend to have similar meanings, as with “I fed my cat” and “I fed my dog”. Models such as Word2Vec and GloVe learned word vectors from those patterns.

Later, transformer-based models such as BERT made embeddings context-aware, so “bank” could get a different vector in “river bank” and “bank loan”. Many modern models embed entire sentences or passages at once. Our guide to large language models explains how transformers handle context.

Where embeddings are used

Use How it helps
Semantic search Finds results by meaning, rather than exact words
RAG Retrieves relevant passages for a language model
Recommendations Suggests items similar to those you liked
Clustering Groups support tickets or reviews by topic
Duplicate detection Spots similar questions or listings
Classification Provides features for simple models
Anomaly detection Flags items far from everything else

Our example compared a query with every item, which is known as an exact search. That works fine for thousands of items, but it becomes slow with many millions. Approximate nearest neighbour (ANN) indexes such as HNSW solve this. They find very close matches quickly, by giving up a small chance of missing the absolute best one.

Vector databases and extensions, such as pgvector for PostgreSQL, provide these indexes along with filtering and storage. Our vector database guide covers the options. For a small project, you may not need one at all, and an in-memory search is a perfectly good way to start.

Tip: Consider hybrid search early. Combining embedding similarity with keyword matching often gives better results than either alone, particularly when users search for product codes, names or error messages.

Choosing an embedding model

  • Language. If your content is in Hindi or Tamil, for example, check that the model handles it well.
  • Domain. General models can struggle with specialist legal or medical terms.
  • Dimensions. More dimensions can capture more detail, but they cost more storage and memory.
  • Cost and hosting. Compare a hosted API with an open model you can run yourself, especially for private data.
  • Input length. Models have a limit, so long documents must be split into chunks first.

The best test is your own data. Collect about 20 real queries along with the documents they should find, and compare how each model ranks them. Published benchmarks may not reflect your content.

Limitations

  • Model lock-in. Vectors from different models cannot be compared. If you change models, you must embed everything again.
  • Exact terms. IDs, codes and rare names may not be matched reliably.
  • Negation. “Refunds allowed” and “refunds not allowed” can land close together, despite meaning opposite things.
  • Similar does not mean correct. A close match may be outdated or simply wrong.
  • Bias. Embeddings can absorb stereotypes from their training data.
  • Cosine is not magic. Researchers have questioned how meaningful it is on some learned embeddings, so treat scores as a useful signal and not as a measure of truth.

Common mistakes

  • Mixing models. Embed queries and documents with the same model and version.
  • Using one similarity threshold everywhere. Tune it for each model and task.
  • Embedding huge documents whole. Meaning gets diluted, so chunk them first.
  • Skipping evaluation. Without test queries, you cannot tell if a change helped.
  • Using embeddings for everything. If users search for exact IDs, a normal database index is better.

Final thoughts

An embedding turns meaning into numbers, so that software can compare ideas rather than strings. The concept is simple, while the practical work lies in choosing a model, chunking data and testing on real queries. To try it yourself, embed a few dozen of your own documents with any embedding model, search them with real questions, and see where the results surprise you.

Frequently asked questions

What are embeddings in simple terms?

Embeddings are lists of numbers that represent meaning. Items with similar meaning get similar numbers, which lets a computer compare them mathematically.

What is cosine similarity?

It measures the angle between two vectors, from -1 to 1. A score near 1 means they point in nearly the same direction, which suggests similar content.

What is the difference between embeddings and tokens?

A token is a chunk of text that a model processes. An embedding is a numeric vector representing the meaning of a token, sentence or document.

How many dimensions should an embedding have?

It depends on the model, and hundreds to a few thousand is common. More can capture more detail, but it costs more storage, so test what your use case needs.

Do I need a vector database to use embeddings?

No. Small collections can be searched in memory. Vector databases become useful with larger collections, frequent updates or filtering needs.

Keep learning

Work with Stack Quora

Need help with testing, WordPress or SEO?

We help teams ship better software and grow their sites. Tell us what you are working on and we will suggest a clear next step.

Keep reading

More guides on testing, AI & modern development

Enjoyed this article? Pick a topic and jump straight into our newest tutorials.

Artificial Intelligence

LLM Structured Output Explained: Getting Reliable JSON From AI Models

LLM structured output explained with validation we ran in Pydantic: JSON mode and schemas, constrained decoding, the ways model output…

Read article →
DevOps

SSH Keys Explained: How They Work, ssh-keygen, Ed25519 and Safe Habits

SSH keys explained with keys we generated: public vs private keys, ssh-keygen, Ed25519 vs RSA, fingerprints, passphrases, ssh-agent, authorized_keys, config…

Read article →
DevOps

HTTP Caching Explained: Cache-Control, ETag and 304 Responses, With a Live Test

HTTP caching explained with a small server we ran: Cache-Control directives, max-age, no-cache vs no-store, ETag revalidation and 304 responses,…

Read article →
Test Automation

API Contract Testing Explained: Catch Breaking Changes Before They Ship

API contract testing explained with a schema check we ran: what a contract is, consumer-driven contracts, Pact, OpenAPI, which changes…

Read article →
Software Testing

Risk-Based Testing Explained: Score Risks, Build a Matrix and Test What Matters

Risk-based testing explained step by step: identifying risks, scoring likelihood and impact, building a risk matrix, mapping risk to test…

Read article →
Databases & SQL

SQL Constraints Explained: NOT NULL, UNIQUE, CHECK and FOREIGN KEY, Tested

SQL constraints explained with tests we ran: NOT NULL, UNIQUE, CHECK, DEFAULT, PRIMARY and FOREIGN KEY, the NULL loopholes, SQLite's…

Read article →
Databases & SQL

SQL Date Functions Explained: Adding, Comparing and Grouping Dates Without Bugs

SQL date functions explained with queries we ran: adding intervals, month-end surprises, date differences, the BETWEEN trap, grouping by day,…

Read article →
Software Development

Python asyncio Explained: async, await, gather and TaskGroup With Real Timings

Python asyncio explained with code we timed: coroutines, await, gather, TaskGroup, error handling, timeouts, limiting concurrency and the blocking-call mistake…

Read article →
Software Development

Debounce vs Throttle in JavaScript: How They Work, When to Use Each, Tested

Debounce vs throttle in JavaScript explained with timings we measured in Node.js: how each works, simple implementations, leading and trailing…

Read article →
Software Development

Python Context Managers Explained: The with Statement, contextlib and Real Uses

Python context managers explained with code we ran: how with works, __enter__ and __exit__, contextlib.contextmanager, suppress, ExitStack, database transactions and…

Read article →
Artificial Intelligence

LLM Temperature and Top-p Explained: How AI Models Choose Their Next Word

LLM temperature and top-p explained with a simulation we ran: how probabilities are reshaped, what top-p keeps, recommended settings by…

Read article →
DevOps

YAML Explained: Syntax, the Norway Problem and Other Traps We Tested

YAML explained with a parser test: basic syntax, lists and maps, the Norway problem, versions turning into floats, octal zip…

Read article →
DevOps

Cron Jobs Explained: Syntax, Real Schedules and the Mistakes That Break Them

Cron jobs explained with schedules we verified: the five fields, operators, real examples, the day-of-month OR day-of-week trap, environment problems,…

Read article →
Software Testing

Bug Life Cycle in Software Testing: Every Status, Who Owns It and Common Mistakes

The bug life cycle explained: every defect status from New to Closed, who owns each step, rejected, deferred and reopened…

Read article →
Test Automation

Property-Based Testing in Python: Hypothesis Finds the Bugs Your Examples Miss

Property-based testing explained with Hypothesis tests we ran: properties vs examples, strategies, shrinking, the real bugs it found in our…

Read article →
Databases & SQL

SQL Pagination Explained: OFFSET vs Keyset, With a Million-Row Test

SQL pagination explained with a million-row test we ran: LIMIT and OFFSET, why deep pages get slow, keyset (cursor) pagination,…

Read article →
Databases & SQL

SQL EXISTS vs IN: Differences, the NOT IN NULL Trap and Performance

SQL EXISTS vs IN explained with queries we ran: how each works, why NOT IN returns no rows when a…

Read article →
Software Development

Python Logging Explained: Levels, Handlers, Formatters and Mistakes to Avoid

Python logging explained with code we ran: levels, basicConfig quirks, named loggers, handlers and formatters, logger.exception, lazy formatting, duplicate logs…

Read article →
Software Development

JavaScript Map vs Object: Key Differences, Pitfalls and a Benchmark

JavaScript Map vs Object explained with code we ran in Node.js: key types, ordering, the prototype trap, JSON, size, WeakMap…

Read article →
Software Development

Python Type Hints Explained: Annotations, mypy and the Errors It Catches

Python type hints explained with a real mypy run: basic annotations, Optional and unions, TypedDict, Literal, Protocol, generics, Callable, strict…

Read article →
Artificial Intelligence

LLM Tokens Explained: How AI Models Read Text, Count Costs and Fill Context Windows

LLM tokens explained with a tokenizer we ran: how text is split, why counts differ by language and tokenizer, context…

Read article →
DevOps

Semantic Versioning Explained: MAJOR.MINOR.PATCH, Ranges and Release Rules

Semantic versioning explained with code we ran: MAJOR.MINOR.PATCH rules, pre-releases, correct version sorting, caret and tilde ranges, 0.x versions, lockfiles…

Read article →
DevOps

Git Reset vs Revert: How to Undo Commits Safely, With a Tested Example

Git reset vs revert explained with a repository we built: soft, mixed and hard reset, revert commits, reverting merges, recovering…

Read article →
Software Testing

The Test Pyramid Explained: Layers, Ratios and When to Break the Shape

The test pyramid explained: unit, integration and end-to-end layers, sensible ratios, the ice cream cone anti-pattern, the testing trophy and…

Read article →
Test Automation

pytest Fixtures Explained: Scope, yield Teardown, conftest.py and Factories

pytest fixtures explained with a suite we ran: how fixtures work, scopes, yield teardown, conftest.py, factory fixtures, parametrized fixtures, built-ins…

Read article →
Databases & SQL

SQL Upsert Explained: INSERT ON CONFLICT, MERGE and the REPLACE Trap

SQL upsert explained with queries we ran: INSERT ON CONFLICT DO UPDATE and DO NOTHING, the excluded row, conditional updates,…

Read article →
Databases & SQL

SQL LIKE Operator Explained: Wildcards, Case Sensitivity and Performance

The SQL LIKE operator explained with queries we ran: % and _ wildcards, escaping, NOT LIKE and NULL, case sensitivity…

Read article →
Artificial Intelligence

AI Hallucinations Explained: Why Language Models Make Things Up and How to Reduce It

AI hallucinations explained: what they are, why language models produce them, common types, how to reduce them with grounding and…

Read article →
Test Automation

Mocking in Python: unittest.mock, patch and the Mistakes That Make Tests Lie

Mocking in Python with unittest.mock, tested: Mock and return_value, side_effect, patching where a name is used, spec and autospec, mocking…

Read article →
Software Testing

Code Coverage Explained: Line vs Branch Coverage and What 100% Really Means

Code coverage explained with a real coverage.py run: line, branch and condition coverage, reading a report, why 100% coverage can…

Read article →
Artificial Intelligence

What Are AI Agents? How They Work, With a Simple Agent Loop You Can Run

What are AI agents? A plain guide to the agent loop, tools, memory and guardrails, with a small Python loop…

Read article →
Test Automation

TDD vs BDD: Key Differences, One Feature Built Both Ways and When to Use Each

TDD vs BDD explained with one discount feature built both ways: red-green-refactor with code we ran, Given-When-Then scenarios, tools, trade-offs…

Read article →
Software Testing

User Acceptance Testing (UAT): Process, Checklist and Real Examples

User acceptance testing explained: who runs it, how it differs from QA, a seven-step process, a UAT test case template,…

Read article →
Artificial Intelligence

What Are Embeddings? Vectors and Cosine Similarity Explained Simply

What are embeddings? A plain-language guide to vectors, cosine similarity and semantic search, with a small example we ran, common…

Read article →
Test Automation

Flaky Tests: What Causes Them and How to Fix Them for Good

Flaky tests explained with demos we ran: the main causes, how to confirm and diagnose one, proper fixes for timing,…

Read article →
Software Testing

Load Testing vs Stress Testing: Differences, Metrics and k6 Examples

Load testing vs stress testing explained: what each proves, spike and soak tests, the metrics that matter, why averages mislead,…

Read article →