Cosine Similarity for Recommendations: How and Why It Works
By Seekora Editor
May 17, 2026

Open any modern product recommendation engine, peel back the layers of inference pipelines and ranking heuristics, and at the core of almost every one is the same modest piece of geometry: the cosine of an angle between two vectors. It is not glamorous. It does not have a marketing campaign. It is also, quietly, what makes "customers also bought" and "complete the look" feel like magic instead of guesswork.
For engineering teams about to ship a recommender or upgrade an existing one, understanding cosine similarity in concrete terms is the difference between treating the model as a black box and being able to debug it, tune it, and know when it is the wrong tool. This piece walks through the why, the math, and the scaling realities — without the panic and without leaning on dense academic notation.
Why every modern recommender ends up at cosine similarity
A recommender's job is to take one thing the shopper is engaging with — a product, a query, a category, a click stream — and return a ranked list of other things in the catalog that resemble it. The hard part is defining "resemble" in a way that scales to millions of SKUs and works across cold-start, long-tail, and richly-described items alike.
Classic collaborative filtering tried to define resemblance through behavior — users who liked A also liked B. It worked, until catalogs got large and signal got sparse. Content-based methods tried to define it through attributes — both items have the brand X and category Y. That worked too, but only for catalogs with clean structured data.
The move to vector embeddings dissolved most of that ceiling. When every product is represented as a fixed-length vector that captures meaning across attributes, imagery, and behavioral signal, the question how similar are these two products? becomes a geometric one — how similar are their vectors? — and geometry has well-understood, fast answers. Cosine similarity is the answer the field converged on, and there are good reasons it stayed.
From products to vectors: the embedding step
Before cosine can do anything, every product has to become a vector. This step matters more than the similarity calculation itself, because no amount of mathematical elegance can save a bad embedding.
A modern embedding pipeline takes each product through a model — usually a transformer or a multimodal encoder — that ingests the product title, description, attributes, imagery, and a slice of behavioral context (queries it wins, items it co-converts with). The output is a list of floating-point numbers, typically 384 to 1536 of them, which together represent the product's position in a high-dimensional semantic space.
The key property is that vectors of semantically similar products end up close together. A leather wallet's vector lives near other leather wallets. A running shoe's vector lives near other athletic footwear. A cycling-themed gift's vector hovers between cycling accessories and gifts. This proximity is what cosine similarity will exploit. The embedding step is also where the most subtle bugs live — drift between the model used at indexing time and the model used at query time, stale embeddings on updated products, or signal pollution from low-quality descriptions all silently degrade the recommender downstream.
The math, without the panic
Cosine similarity measures the cosine of the angle between two vectors. That's it. The formula collapses to a dot product divided by the product of the two vector magnitudes:
similarity(A, B) = (A · B) / (||A|| · ||B||)
The value lives between -1 and 1. A value near 1 means the two vectors point in nearly the same direction — very similar. A value near 0 means they are orthogonal — unrelated. A value near -1 means they point in opposite directions — antithetical, though in practice product embeddings rarely produce negative values because most products share at least some shared signal.
The geometric intuition is easier than the algebra. Imagine every product as an arrow planted at the origin of a high-dimensional space, pointing in some direction. Two arrows pointing the same way — regardless of how long they are — score near 1. Two arrows pointing perpendicular ways score 0. Two pointing opposite directions score -1. The cosine doesn't care how long the arrows are, only what direction they point.

That direction-only property is the deep reason cosine became the default. Two product vectors can differ wildly in magnitude — one product might have more reviews, longer descriptions, richer imagery — but if the direction of the vectors is similar, the products are conceptually similar. Cosine respects the meaning while ignoring the noise of how much content happens to exist for each item.
Why cosine, not Euclidean
Euclidean distance — the straight-line distance between two points — is the obvious alternative, and the question why not Euclidean? comes up in every onboarding discussion. The short answer: magnitude matters with Euclidean, and magnitude is usually misleading.
A product with a 500-word description and a product with a 30-word description will often have vectors of different magnitudes, even when they represent semantically similar items. Euclidean distance treats them as far apart because of that magnitude gap. Cosine ignores the gap and asks the more useful question — do they point the same way?
There are scenarios where Euclidean is the right call (clustering with bounded vector norms, or when magnitude itself carries signal), but for retail recommendation, cosine wins on robustness across messy catalogs.
There is also a practical bonus. If you normalize your vectors to unit length before storing them — which most embedding pipelines do — cosine similarity collapses to a plain dot product. That single optimization is the foundation of every fast similarity index in production today.
Scaling cosine to a real catalog
The naïve implementation — compute cosine between the query vector and every product vector in the catalog — works fine for ten thousand items and falls over by a million. A catalog with five million products and a 10ms latency budget cannot do five million dot products per request. The field's answer is approximate nearest neighbor search, and the dominant technique in production today is HNSW (Hierarchical Navigable Small World graphs).
HNSW pre-builds a layered graph where each product is connected to its closest neighbors at increasing zoom levels. At query time, the graph lets the search start coarse and refine, hopping from one neighborhood to a closer one until it finds the top-K matches — typically in single-digit milliseconds, while inspecting only a small fraction of the catalog.
The trade-off is approximate. HNSW occasionally misses the absolute best match in favor of a near-best one, and the recall-versus-latency knobs are tuned per use case. For commerce, the trade-off almost always works — the difference between the #1 and #4 most similar product is usually marginal, and the latency win is worth the small recall hit. For domains where exactness matters (legal document retrieval, drug discovery), exact dot-product indexes are still in use.
A few practical lessons from running HNSW at retail scale: keep the embedding dimension modest (384 to 768 is usually enough; bigger is rarely worth the cost), version your embeddings so retraining doesn't break the graph, and watch out for cardinality skew where popular products dominate the neighborhood. The full set of patterns for retailers building a vector-based recommender is covered in the seekora recommendations product overview and the engineering details in the developer quick-start.
When cosine alone isn't enough
Cosine gets a recommender 80% of the way there. The last 20% — the part where the recommender starts feeling personal and merchandised, not just geometrically close — comes from layers cosine doesn't touch.
Business rules and inventory constraints have to be applied after the similarity step. A product can be cosine-perfect and out of stock; the system needs to demote it. Personalization signals — the shopper's history, segment, and recent intent — bend the ranking on top of the raw similarity, often through a learned reranker that combines cosine score with a dozen other features. Diversity heuristics prevent the recommender from returning ten near-identical SKUs that all happen to be close to the anchor product. And finally, freshness and recency boosts keep the recommender from showing the same evergreen winners on every page view.
The takeaway for engineering teams is that cosine similarity is the substrate, not the entire recommender. Treat it as the fast, reliable, geometric layer that produces a high-quality candidate set, and stack the merchandising and personalization logic on top. Teams that try to encode all of merchandising into the embedding itself end up with brittle models that resist retraining; teams that try to bypass cosine in favor of pure rules end up with stale, magic-free recommendations.
Wrapping up: what to remember
Three things stick after every cosine deep-dive worth doing. First, the math is small — a dot product over normalized vectors — and the elegance is real. Second, embeddings carry most of the weight; cosine is just the way to read them. Third, scaling is solved at the index layer with HNSW, and the trade-offs are well understood.
For anyone shipping or evaluating a recommender, the most important shift is to stop treating cosine similarity as a vendor abstraction and start treating it as a transparent geometric primitive. Once the team can sketch the dot product on a whiteboard, debate the embedding dimension, and explain why HNSW is approximate, every conversation about ranking quality gets sharper. That clarity, more than any single algorithmic upgrade, is what compounds into recommenders that quietly outperform the ones built by teams who never bothered to look under the hood.
Cosine Similarity for Recommendations: How and Why It Works
Top stories
Top stories
Stay ahead of the curve.
Subscribe to get the latest e-commerce insights and AI strategies delivered directly to your inbox.



