Item-based collaborative filtering
Фильмы похожи, если их оценивали одни и те же люди (косинус по столбцам матрицы взаимодействий). Никаких жанров — только поведение.
score — внутренний ранжировочный балл (сумма похожестей соседей, взвешенная твоими оценками). Это не рейтинг 1–5, не вероятность и не «качество» фильма; сравнивать его можно только внутри одного пользователя и одной модели.
Жанры на карточках показаны только для интерпретации в интерфейсе — модель их не использует (item-CF смотрит лишь на поведение).
Матрица сходства: топ-15 популярных фильмов
Ярче клетка — сильнее «совместная» похожесть (часто оцениваются вместе). Диагональ приглушена (сходство фильма с самим собой = 1). Наведи на клетку — покажет точное значение.
загрузка матрицы…
Осторожно с интуицией. У популярных фильмов сходство часто завышено: их смотрит широкая аудитория, поэтому у них много общих зрителей — но это не всегда настоящая вкусовая близость, а popularity bias. Именно поэтому нужны shrinkage (мы уже применяем, λ=20), centering и осторожная интерпретация cosine.
Теория простым языком
Коллаборативная фильтрация рекомендует на основе поведения людей, а не описаний. Главная мысль: «кто смотрел этот фильм, часто смотрел и вот этот» — значит, эти два фильма похожи, даже если у них разные жанры.
Чем это отличается от content-based
Content-based смотрел внутрь фильма (жанры, теги). Здесь мы про содержание фильма не знаем ничего — смотрим только на то, кто что оценил. Этот подход так и называется:
Бывает двух видов: смотреть на похожих пользователей («такие же люди, как ты, любят…») или на похожие айтемы («к этому фильму обычно идёт вот этот»). Мы реализовали второй — item-based. Его выбирают не потому, что айтемов «меньше» (у нас как раз наоборот: 610 пользователей и 9724 фильма), а потому что свойства айтема стабильнее вкусов человека: похожесть двух фильмов меняется медленно, поэтому её можно предрассчитать offline и периодически обновлять, тогда как профиль пользователя плывёт постоянно.
▸Как CF вырос из корреляции
Когда два фильма считаются похожими
Вернёмся к матрице «пользователи × фильмы» с оценками. Каждый фильм — это столбец: список того, кто и как его оценил. Похожесть двух фильмов — это похожесть их столбцов.
Как получаются рекомендации
1. Берём фильмы, которые ты хорошо оценил (rating ≥ 4) — только лайки; низкие оценки в профиль не идут, чтобы нелюбимое не тянуло рекомендации вверх.
2. Для каждого находим самые похожие на него (по столбцам матрицы).
3. Складываем эти «голоса»: фильм, похожий сразу на несколько твоих любимых, получает высокий балл. Верхние по баллу и показываем (убрав уже виденные).
Если у пользователя нет оценок ≥ 4, откатываемся на всю его историю (иначе рекомендовать было бы не от чего). На MovieLens этот positive-only профиль ещё и повышает точность против «взять все оценки как вес».
# офлайн: считаем похожести между всеми парами фильмов один раз
нормируем каждый столбец-фильм матрицы оценок
sim[i][j] = cosine(столбец i, столбец j)
recommend(пользователь u, K):
score = {}
для каждого фильма j, который u оценил ≥ 4 (лайки):
для каждого соседа i из топ-похожих на j:
score[i] += sim[i][j] * r[u][j]
# если лайков нет — берём всю историю u (fallback)
убрать из score уже виденные u фильмы
вернуть K фильмов с наибольшим score▸Формула: похожесть и итоговый балл
Похожесть фильмов и — косинус их столбцов оценок и :
Балл фильма для пользователя — сумма похожестей на то, что лайкнул (оценка ≥ 4), взвешенная этими оценками:
Так фильмы с низкой оценкой не дают вклада вовсе. Альтернативы: взять все оценки как вес (тогда 2★ даёт малый положительный вклад — методологически спорно) или центрировать (ниже среднего → минус). На наших данных positive-only оказался и чище, и точнее.
На практике сумму берут не по всем , а только по k ближайшим соседям фильма — отсюда «kNN».
Главные сложности
Несмотря на это, на наших данных item-CF в этом протоколе обыгрывает popularity по ranking-метрикам (hit_rate@10 / recall@10 / ndcg@10). Это и есть выигрыш персонализации: рекомендации зависят от того, что смотрел именно ты.
Сильные стороны
- Реальная персонализация: учитывает твою историю.
- Не нужны описания айтемов — достаточно поведения (но его надо корректно логировать: логи грязные, смещённые, без impressions — см. B1).
- Похожести можно предрассчитать offline и периодически обновлять → рекомендации быстрые.
Слабые стороны
- Холодный старт: бесполезна для новых пользователей и айтемов.
- Разреженность данных бьёт по качеству.
- Память и вычисления растут с числом айтемов (похожести между всеми парами).
⚠️ Что может пойти не так
- Разреженность: у двух фильмов может быть всего пара общих зрителей — косинус по такой выборке случаен. Помогает усадка (shrinkage): чем меньше общих оценок n_ij, тем сильнее похожесть тянут к нулю множителем n_ij/(n_ij+λ). Здесь это уже применено (λ=20): на офлайн-метриках ndcg@10 вырос с 0.037 до 0.041, hit_rate@10 — с 0.077 до 0.088.
- Косинус по сырым оценкам смещён: щедрый зритель, который всем ставит 5, раздувает похожести. Часто оценки сначала центрируют (вычитают среднее пользователя).
- Холодный старт: новый фильм не с чем сравнить, новому пользователю — нечего складывать.
- Популярные фильмы лезут в соседи ко всему подряд (их многие смотрели), поэтому без поправок item-CF тоже подвержен popularity bias.
🧠 Проверь себя: Почему на практике берут именно item-based, а не user-based вариант?