Метрики похожести
Урок разбирает три ключевые меры похожести — косинус, корреляцию Пирсона и Жаккар — и объясняет, когда какая уместна.
Мера похожести — это число, показывающее, насколько близки два вектора предпочтений; именно она определяет, кого CF считает «соседом».
Косинусная похожесть
Самая популярная мера. Она смотрит на угол между двумя векторами оценок, игнорируя их длину. Два пользователя похожи, если их предпочтения «направлены» одинаково, даже если один ставит оценки строже. Значение от 0 (ортогональны) до 1 (совпадают по направлению).
import math
def cosine(a, b):
common = set(a) & set(b)
if not common:
return 0.0
num = sum(a[i] * b[i] for i in common)
na = math.sqrt(sum(v * v for v in a.values()))
nb = math.sqrt(sum(v * v for v in b.values()))
return num / (na * nb) if na and nb else 0.0
u1 = {"Матрица": 5, "Титаник": 1, "Аватар": 4}
u2 = {"Матрица": 4, "Титаник": 1, "Аватар": 5}
u3 = {"Матрица": 1, "Титаник": 5, "Аватар": 2}
print("cos(u1,u2) =", round(cosine(u1, u2), 3))
print("cos(u1,u3) =", round(cosine(u1, u3), 3))Вывод:
cos(u1,u2) = 0.976 cos(u1,u3) = 0.507
u1 и u2 почти совпадают (близко к 1), а u3 с противоположными вкусами заметно дальше.
Корреляция Пирсона
Косинус не учитывает, что у людей разный «нулевой уровень»: оптимист ставит всем 4–5, скептик — 2–3. Пирсон сначала вычитает у каждого его среднюю оценку (центрирует), а потом сравнивает отклонения. Так мы сравниваем не абсолютные оценки, а паттерн «что выше, что ниже личного среднего».
import math
def pearson(a, b):
common = set(a) & set(b)
n = len(common)
if n == 0:
return 0.0
sa = sum(a[i] for i in common); sb = sum(b[i] for i in common)
saa = sum(a[i]**2 for i in common); sbb = sum(b[i]**2 for i in common)
sab = sum(a[i]*b[i] for i in common)
num = sab - sa*sb/n
den = math.sqrt((saa - sa*sa/n) * (sbb - sb*sb/n))
return num/den if den else 0.0
x = {"m1": 5, "m2": 3, "m3": 4, "m4": 4}
y = {"m1": 3, "m2": 1, "m3": 2, "m4": 3, "m5": 3}
print("Pearson =", round(pearson(x, y), 3))Вывод:
Pearson = 0.853
Коэффициент Жаккара
Когда фидбэк неявный (был клик / не было), оценок нет — есть только множества объектов. Тогда подходит Жаккар: доля общих объектов среди всех, с которыми взаимодействовал хоть кто-то из двоих.
def jaccard(a, b):
a, b = set(a), set(b)
return len(a & b) / len(a | b) if (a | b) else 0.0
print("Jaccard =", round(jaccard({"A","B","C"}, {"B","C","D","E"}), 3))Вывод:
Jaccard = 0.4
Как работает под капотом и когда что брать
| Мера | Учитывает | Когда применять |
| Косинус | Направление вектора оценок | Явные оценки, разная «строгость» терпима |
| Пирсон | Отклонения от личного среднего | Явные оценки, разный уровень оптимизма |
| Жаккар | Пересечение множеств | Неявный фидбэк без оценок |
Частые ошибки
- Считать похожесть по одному общему объекту. Один товар даёт мнимую похожесть 1.0; вводите минимум общих элементов.
- Применять косинус к «оптимистам и скептикам». Если уровни оценок сильно сдвинуты, центрированный Пирсон честнее.
- Использовать оценочные меры для неявных данных. Без оценок берите Жаккар или косинус по бинарным векторам.
Итоги
- Косинус сравнивает направление векторов оценок, игнорируя их длину.
- Пирсон центрирует оценки и убирает влияние разного «нулевого уровня».
- Жаккар работает с множествами и подходит для неявного фидбэка.
- Меру выбирают под тип данных; одного общего объекта для похожести мало.