Матрица взаимодействий пользователь-товар

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

Матрица взаимодействий — таблица, где строки это пользователи, столбцы это товары, а в ячейке стоит сигнал (оценка, факт покупки или пусто, если взаимодействия не было).

Как выглядит задача

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

          Матрица  Титаник  Аватар  Шрек
Аня          5        2       4      1
Борис        4        1       5      ?
Вера         1        5       ?      4
Глеб         5        ?       4      1

Знаки ? — это то, что мы хотим предсказать. Если мы оценим, что Борис поставил бы «Шрек» низкую оценку, мы не порекомендуем ему «Шрек».

Разреженность — главная проблема

В реальности у платформы миллионы пользователей и миллионы товаров, а каждый человек взаимодействовал в лучшем случае с сотнями объектов. Значит, заполнено в матрице порядка 0,01–0,1% ячеек, а 99,9% — пусты. Это и есть разреженность, и она усложняет всё: похожих пользователей мало кто пересекает по товарам, статистики по каждой паре почти нет, а наивный перебор по полной матрице невозможен из-за размера.

R = [
    [5, 2, 4, 0],
    [4, 1, 5, 0],
    [1, 5, 0, 4],
    [5, 0, 4, 1],
]
total = len(R) * len(R[0])
filled = sum(1 for row in R for v in row if v != 0)
sparsity = 1 - filled / total
print(f"Всего ячеек: {total}")
print(f"Заполнено:   {filled}")
print(f"Разреженность: {sparsity:.0%}")

Вывод:

Всего ячеек: 16
Заполнено:   12
Разреженность: 25%

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

На практике матрицу никогда не хранят как плотный двумерный массив — это сожрало бы терабайты пустоты. Хранят только ненулевые элементы: список троек (пользователь, товар, сигнал) или словари. Все алгоритмы курса работают именно с разреженным представлением, обходя лишь известные взаимодействия.

# разреженное хранение: только известные ячейки
interactions = {
    ("Аня", "Матрица"): 5,
    ("Аня", "Титаник"): 2,
    ("Борис", "Матрица"): 4,
    ("Борис", "Аватар"): 5,
}
for (u, i), r in interactions.items():
    print(f"{u} оценил(а) '{i}' на {r}")

Вывод:

Аня оценил(а) 'Матрица' на 5
Аня оценил(а) 'Титаник' на 2
Борис оценил(а) 'Матрица' на 4
Борис оценил(а) 'Аватар' на 5

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

  • Хранить матрицу плотно. При миллионах строк и столбцов это невозможно; используйте разреженные структуры.
  • Считать 0 «плохой оценкой». В матрице 0 (или пусто) почти всегда означает «нет данных», а не «не понравилось».
  • Недооценивать разреженность. Многие интуитивно «очевидные» методы рассыпаются, когда пересечений между пользователями почти нет.

Итоги

  • Задача рекомендаций — заполнение пропусков в матрице пользователь-товар.
  • Реальные матрицы заполнены на доли процента: это и есть разреженность.
  • Хранят только известные взаимодействия, а не плотный массив.
  • Пустая ячейка — это «нет данных», а не «низкая оценка».
Проверьте себя
1. Что такое разреженность матрицы взаимодействий?
AДоля заполненных ячеек очень высока
BПодавляющее большинство ячеек пусты (нет взаимодействий)
CМатрица квадратная
DВсе оценки равны нулю
2. Как обычно хранят матрицу взаимодействий в продакшене?
AКак плотный двумерный массив целиком
BТолько ненулевые элементы — список троек или словари
CВ виде картинки
DНе хранят вовсе