Метрики похожести

Урок разбирает три ключевые меры похожести — косинус, корреляцию Пирсона и Жаккар — и объясняет, когда какая уместна.

Мера похожести — это число, показывающее, насколько близки два вектора предпочтений; именно она определяет, кого 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; вводите минимум общих элементов.
  • Применять косинус к «оптимистам и скептикам». Если уровни оценок сильно сдвинуты, центрированный Пирсон честнее.
  • Использовать оценочные меры для неявных данных. Без оценок берите Жаккар или косинус по бинарным векторам.

Итоги

  • Косинус сравнивает направление векторов оценок, игнорируя их длину.
  • Пирсон центрирует оценки и убирает влияние разного «нулевого уровня».
  • Жаккар работает с множествами и подходит для неявного фидбэка.
  • Меру выбирают под тип данных; одного общего объекта для похожести мало.
Проверьте себя
1. Чем корреляция Пирсона отличается от косинусной похожести?
AПирсон работает только с текстами
BПирсон центрирует оценки, убирая влияние разного личного среднего
CОни полностью эквивалентны
DКосинус учитывает среднее, а Пирсон — нет
2. Какую меру похожести логичнее всего взять для чисто неявного фидбэка (был клик / не было)?
AКорреляцию Пирсона по оценкам
BКоэффициент Жаккара по множествам объектов
CСреднее арифметическое оценок
DНикакую — CF тут не работает