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).
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:
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.