Skip to content
Proposal for a new site by Genesis Innovation, built from his public LinkedIn profile. Not his official site.

Wiki Machine learning

Recommendation by cosine similarity

Measuring how much two rating vectors point in the same direction and using that to recommend items that a similar user already liked.

Cosine similarity measures the angle between two vectors: (a·b) / (|a|·|b|). It is 1 when they point in the same direction, 0 when they are orthogonal (no items in common, with non-negative ratings). In recommendation, each user is a vector of ratings per item; users with close vectors tend to like the same things.

Recommending from the most similar neighbor

The example computes Ana's similarity to the other users and suggests the movies that the most similar one rated and she has not seen yet (rating 0 = not seen).

PythonOutput checked by running Python 3
from math import sqrt

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

ratings = {
    "Ana": [5, 4, 0, 0],
    "Bia": [5, 5, 4, 0],
    "Caio": [0, 1, 5, 5],
}
target = "Ana"
similar = {n: round(cosine(ratings[target], v), 2) for n, v in ratings.items() if n != target}
print(similar)
neighbor = max(similar, key=similar.get)
new = [i for i, (a, v) in enumerate(zip(ratings[target], ratings[neighbor])) if a == 0 and v > 0]
print(neighbor, "suggests the movies at index", new)

Output (checked)

{'Bia': 0.87, 'Caio': 0.09}
Bia suggests the movies at index [2]

Pitfall

Cosine ignores magnitude: it only counts direction. Two proportional vectors have similarity 1 even when they are far apart:

PythonOutput checked by running Python 3
from math import sqrt

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

small = [1, 2]
large = [100, 200]
print(round(cosine(small, large), 2))
print(round(sqrt(sum((x - y) ** 2 for x, y in zip(small, large))), 1))

Output (checked)

1.0
221.4

Someone who rated 1 and 2 and someone who rated 100 and 200 have the same "taste" as far as cosine is concerned. That is desirable for text (a long and a short document about the same subject) and can be a mistake when intensity matters. Another caution: the 0 for "not seen" enters the calculation as a low rating; center the ratings on each user's mean.

Sources

Shared knowledge from Genesis Innovation. Not a claim about the person behind this site.