Item-based коллаборативная фильтрация

Урок разбирает item-based коллаборативную фильтрацию — подход, который сделал знаменитым Amazon, — и объясняет, почему похожесть товаров стабильнее похожести пользователей.

Item-based CF рекомендует товары, похожие на те, с которыми пользователь уже взаимодействовал; похожесть товаров считается по тому, какие пользователи их выбирали.

Сдвиг точки зрения

Вместо «найди похожих на меня людей» item-based говорит «найди товары, похожие на те, что мне понравились». Два товара считаются похожими, если их выбирали примерно одни и те же пользователи. Если все, кто купил «хлеб», брали и «молоко», то «хлеб» и «молоко» похожи по поведению — и тому, кто взял хлеб, стоит предложить молоко.

Именно так работал классический рекомендатель Amazon, описанный в их статье про item-to-item collaborative filtering. Это до сих пор один из самых надёжных промышленных подходов.

Реализация

Для неявных данных удобно считать похожесть товаров как косинус по множествам пользователей, которые с ними взаимодействовали.

import math
from collections import defaultdict

data = {
    "u1": ["хлеб", "молоко", "масло"],
    "u2": ["хлеб", "молоко"],
    "u3": ["хлеб", "молоко", "сыр"],
    "u4": ["пиво", "чипсы"],
    "u5": ["пиво", "чипсы", "орехи"],
}

item_users = defaultdict(set)
for u, items in data.items():
    for it in items:
        item_users[it].add(u)

def cosine_sets(a, b):
    inter = len(a & b)
    return inter / (math.sqrt(len(a)) * math.sqrt(len(b))) if a and b else 0.0

target = "хлеб"
sims = {it: cosine_sets(item_users[target], item_users[it])
        for it in item_users if it != target}
print(f"Похожие на '{target}':")
for it in sorted(sims, key=lambda x: -sims[x]):
    print(f"  {it}: {round(sims[it], 3)}")

Вывод:

Похожие на 'хлеб':
  молоко: 1.0
  масло: 0.577
  сыр: 0.577
  пиво: 0.0
  чипсы: 0.0
  орехи: 0.0

«Молоко» брали все, кто брал «хлеб», — похожесть максимальна. «Пиво» и «чипсы» из другого кластера покупателей — похожесть ноль.

Почему item-based стабильнее

У этого подхода два больших практических плюса.

  • Товаров обычно меньше, чем пользователей, и их каталог меняется медленнее. Похожести «товар-товар» можно посчитать офлайн и долго переиспользовать.
  • Похожесть товаров устойчива во времени. Связь «хлеб ↔ молоко» не меняется от того, что у одного пользователя сменилось настроение. А вот похожесть пользователей «плывёт» с каждым новым кликом.

Поэтому рекомендация в рантайме сводится к дешёвой операции: взять товары из истории пользователя и подтянуть к каждому его заранее посчитанных «соседей».

Как работает под капотом

Прогноз интереса к товару i для пользователя u считают так: берут товары j, которые u уже оценил, и складывают их оценки с весами sim(i, j). Матрицу похожести товаров строят офлайн (часто оставляя для каждого товара лишь top-k соседей, чтобы она была компактной), а онлайн лишь складывают предпосчитанное — отсюда низкая задержка.

Частые ошибки

  • Путать «похоже» и «популярно». Очень популярный товар похож на всё подряд по сырому пересечению; нормируйте косинусом, иначе всем будете рекомендовать бестселлеры.
  • Хранить полную матрицу товар-товар. Для большого каталога держат только top-k соседей на товар.
  • Пересчитывать похожести в рантайме. Их считают офлайн; онлайн — только сложение весов.

Итоги

  • Item-based CF рекомендует товары, похожие на уже понравившиеся.
  • Похожесть товаров считается по пересечению их аудиторий.
  • Подход стабильнее и дешевле user-based: товаров меньше и связи устойчивее.
  • Похожести считают офлайн (top-k), а онлайн лишь складывают предпосчитанное — так работал Amazon.
Проверьте себя
1. Почему item-based CF обычно стабильнее и дешевле user-based?
AТоваров обычно меньше, чем пользователей, и связи между товарами меняются медленнее
BТовары не нужно хранить
CItem-based вообще не использует похожесть
DПользователи не кликают по товарам
2. Почему сырое пересечение аудиторий — плохая мера похожести товаров?
AОно слишком быстрое
BОчень популярный товар «похож» на всё подряд, поэтому нужна нормировка (косинус)
CОно требует оценок
DОно работает только для пользователей