Matrix Factorization (ALS)

Каждому пользователю и фильму выучивается короткий вектор «скрытых черт». Совпадение векторов = интерес. Черты модель находит сама.

Загружаем данные

Карта латентных факторов: топ-400 фильмов

Каждая точка — фильм, спроецированный из пространства скрытых черт в 2D (PCA), цвет — основной жанр; жанров модель не знала, только кто что смотрел. Цвета перемешаны — и это нормально: насколько структура по жанрам вообще есть, картинкой не решается. Замер — сразу под ней.

Загружаем данные

А кластеры-то есть? Проверяем счётом

Загружаем данные

Теория простым языком

Matrix Factorization выучивает каждому пользователю и каждому фильму короткий набор чисел — «скрытые черты». Совпали черты человека и фильма → фильм ему зайдёт. Эти черты компьютер находит сам, никто их заранее не задаёт.

С чего начинаем

Снова берём матрицу «пользователи × фильмы» с оценками — почти пустую. Идея MF: эту огромную пустую таблицу можно приблизительно «собрать» из двух маленьких табличек. Это и есть факторизация.

Факторизация — разложение одного большого объекта на произведение нескольких поменьше. Как число 12 = 3 × 4, так и большую матрицу оценок приближаем произведением двух узких матриц.
▸Почему neighborhood CF стало не хватать
БылоItem-CF искал похожие айтемы через cosine/Pearson.
ПроблемаSimilarity локальна, шумна на разреженных данных и плохо обобщает на неувиденные пары.
ИдеяПредставить пользователя и айтем в общем скрытом пространстве и предсказывать интерес их скалярным произведением.
Стало лучшеПлотные эмбеддинги, обобщение, можно находить неочевидные связи, один score для любой пары «известный пользователь × известный айтем».
Осталось слабымХуже объяснимость, нужен тренинг/регуляризация, сам score — не вероятность.
ДальшеBPR (ранжирование) и two-tower (нейро-retrieval).
▸Почему implicit ALS
ПроблемаЯвные оценки (звёзды) редки, а поведенческие логи массовые.
ИдеяALS разделяет preference puip_{ui} (взаимодействие наблюдалось / нет) и confidence cuic_{ui} (сила уверенности, c=1+αrc = 1 + \alpha r). Важно: pui=1p_{ui}=1 значит «мы видели взаимодействие», а не «понравилось». Ниже — почему на MovieLens это расхождение обходится дорого.

Что такое «скрытые черты»

Латентный фактор (скрытая черта) — одно из чисел в коротком векторе пользователя или фильма. «Латентный» = скрытый: мы не говорим, что это «доля комедийности» — модель сама нащупывает полезные измерения. Но часто они получаются осмысленными.

Допустим, черт всего 2. Тогда у фильма есть пара чисел (например, «детское ↔ взрослое» и «лёгкое ↔ серьёзное»), и у человека — пара чисел про его вкус по тем же осям. Реально мы берём не 2, а несколько десятков таких черт.

Эмбеддинг (вектор-представление) — короткий вектор скрытых черт объекта. «Эмбеддинг фильма» — это его координаты в пространстве вкусов. Похожие фильмы оказываются рядом.

Как предсказываем интерес

Берём вектор пользователя и вектор фильма и считаем их «совпадение» — перемножаем по чертам и складываем (скалярное произведение). Чем больше совпали знаки и величины, тем выше предсказанный интерес. Так одним умножением получаем оценку для любой пары человек–фильм, даже если он его не видел.

Пример. У фильма черта «детское» большая, у взрослого зрителя она отрицательная → произведение уходит в минус → фильм не порекомендуем. У ребёнка та же черта положительная → совпадение → порекомендуем.

Важно: мы не предсказываем звёзды

Наивная MF пытается угадать сам балл (явный фидбек). Мы используем другую, более практичную версию — для неявного фидбека: нам важно не «сколько звёзд», а было ли взаимодействие и насколько мы в нём уверены.

Явный → неявный фидбек — вместо «оценка = 4.5» мы говорим: «факт интереса = да/нет» плюс «уверенность». Высокая оценка не делает целью «угадать 4.5» — она лишь задаёт, насколько сильно эта пара тянет лосс. Знак при этом не меняется: любое наблюдённое взаимодействие идёт в цель как единица.

Поэтому оценку rr превращают в два числа: предпочтение pp (1, если человек вообще трогал фильм, иначе 0) и уверенность c=1+αrc = 1 + \alpha r (чем выше оценка, тем сильнее сигнал). Модель старается сильнее там, где уверенность выше.

Тут зарыт подвох: 1★ — тоже «позитив»

Схему Hu–Koren–Volinsky придумали для неявного фидбека, где rr — это счётчик: сколько раз человек слушал трек, сколько минут смотрел. Там «больше rr» действительно значит «сильнее сигнал», а отрицательного сигнала в логе просто нет.

У нас rr — это звёзды, включая плохие. Единица тоже даёт p=1p=1, только с меньшим весом: фильм, который человеку не понравился, попадает в цель обучения как предпочтение. А оцениваем мы при этом строго обратное — релевантным считается таргет с оценкой ≥ 4. Модель учат на одном, а спрашивают другое.

Насколько это дорого — вопрос замера, а не мнения. Обучим ту же ALS ещё раз, оставив позитивами только лайки (оценка ≥ 4), и сравним на тех же пользователях и том же протоколе:

Загружаем данные

Откуда берутся числа: ALS

Числа подбирают так, чтобы предсказания как можно лучше совпадали с реальными предпочтениями. Делают это хитрым повторяющимся приёмом:

ALS (Alternating Least Squares) — «поочерёдный метод наименьших квадратов». Замораживаем векторы фильмов и идеально подбираем векторы людей; потом наоборот — замораживаем людей и подбираем фильмы. Повторяем туда-сюда, пока не сойдётся. На каждом шаге задача простая и решается точно.
Псевдокод (implicit ALS)
p[u][i] = 1, если u трогал i, иначе 0
c[u][i] = 1 + alpha * r[u][i]      # уверенность

инициализируем X (люди) и Y (фильмы) случайно
повторяем N раз:
    при фиксированном Y подбираем каждый x[u] точно (МНК)
    при фиксированном X подбираем каждый y[i] точно (МНК)

score(u, i) = x[u] · y[i]          # для рекомендаций
▸Формулы: предсказание, цель, шаг ALS

Предсказанный интерес — скалярное произведение векторов:

r^ui=xu⊤yi\hat{r}_{ui} = x_u^\top y_i

Цель для неявного фидбека (Hu–Koren–Volinsky): подгоняем r^ui\hat{r}_{ui} к предпочтению puip_{ui}, но каждую ошибку взвешиваем уверенностью cui=1+αruic_{ui}=1+\alpha r_{ui}:

min⁡X,Y ∑u,icui (pui−xu⊤yi)2+λ(∑u∥xu∥2+∑i∥yi∥2)\min_{X,Y}\ \sum_{u,i} c_{ui}\,\big(p_{ui} - x_u^\top y_i\big)^2 + \lambda\Big(\sum_u \lVert x_u\rVert^2 + \sum_i \lVert y_i\rVert^2\Big)

где λ\lambda — регуляризация (штраф за большие веса, против переобучения). При фиксированном YY каждый вектор пользователя находится точно:

xu=(Y⊤CuY+λI)−1Y⊤Cupux_u = \big(Y^\top C^u Y + \lambda I\big)^{-1} Y^\top C^u p_u

CuC^u — диагональная матрица уверенностей пользователя uu. Симметрично считают yiy_i при фиксированном XX. Это и есть «поочерёдность» ALS.

Визуализация рядом — и чего она НЕ доказывает

Эмбеддинги фильмов многомерные, глазом не увидеть. Поэтому их сжимают в 2D и рисуют точками. Дальше начинается ловушка: на такой картинке очень хочется увидеть смысл — «мультики к мультикам, ужасы к ужасам» — и объявить, что модель выучила жанры. Здесь раньше это и было написано.

Но картинка этого не доказывает, и сразу по двум причинам. Во-первых, цвет добавлен постфактум из жанра: раскрасив точки чем угодно, мы увидим в облаке ровно то, чем раскрасили. Во-вторых, двумерная проекция удерживает малую часть разброса — то, что в ней рядом, в исходном пространстве может быть далеко. Правильный ход — посчитать долю соседей того же жанра в полном пространстве и сравнить её со случайной расстановкой меток. Мы так и делаем, и замер выше говорит: структура есть и она не случайна, но «кластеры по жанрам» — это преувеличение.

Снижение размерности (PCA) — способ заменить длинный вектор на два числа так, чтобы сохранить как можно больше разброса точек: первая ось — направление наибольшей дисперсии, вторая — наибольшей из того, что осталось. Это не «сохранить, кто от кого далеко»: попарные расстояния PCA не оптимизирует и в общем случае искажает. Нужен, только чтобы нарисовать многомерное на плоскости.

Сильные стороны

  • Сильная персонализация и хорошее качество при разреженных данных.
  • Сжатые векторы → быстрый поиск похожих и рекомендаций.
  • Латентные черты обобщают: ловят вкус там, где «совпавших зрителей» в лоб нет.

Слабые стороны

  • Холодный старт остаётся: новому пользователю/фильму вектор взять неоткуда.
  • Черты трудно объяснить словами («почему именно это?»).
  • Нужно подбирать гиперпараметры (число черт, регуляризацию, alpha).

⚠️ Что может пойти не так

  • Путать с предсказанием оценки: implicit-MF учится «было/не было + уверенность», а не «сколько звёзд». Считать её регрессией на рейтинг — концептуальная ошибка.
  • Слишком мало факторов → модель недоучивается; слишком много без регуляризации → переобучение под train.
  • Эмбеддинги случайно инициализируются: оси сами по себе ничего не значат, а знаки/повороты от запуска к запуску разные — нельзя толковать «фактор 3 = комедийность».
  • Читать смысл с двумерной проекции: цвет на ней добавлен постфактум, а расстояния искажены сжатием. Утверждение «похожее рядом» проверяется счётом в полном пространстве и сравнением со случайной расстановкой меток — иначе это разглядывание облака.
  • Популярность всё ещё протекает: без поправок частые фильмы получают вектор с большой нормой и всплывают у многих.
  • Цель обучения и определение релевантности могут не совпадать: p_ui = 1 для любой выставленной оценки, а в метрике релевантно только ≥ 4. Схема из статьи рассчитана на счётчики (прослушивания), где плохих значений не бывает; на звёздах это надо проверять отдельно.

🧠 Проверь себя: Что на самом деле оптимизирует наша implicit-MF?

Конфигурация обучения

Всё, что нужно, чтобы повторить числа на этой странице. Значения читаются из самих обученных моделей, а не набраны в вёрстке, — разойтись с кодом они не могут.

Загружаем данные

Что дальше

До финальной сборки не хватает ещё 11 модулей по этому пути.

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

Источники