BPR — pairwise ranking
Та же matrix factorization, но обучена ранжировать: ставить фильмы, с которыми ты взаимодействовал, выше случайных. Оптимизируем порядок напрямую — это мостик к learning-to-rank.
Карта латентных факторов: топ-400 фильмов
Каждая точка — фильм в пространстве скрытых черт, спроецированный в 2D (PCA), цвет — основной жанр. Как и у MF, кластеры складываются сами из того, кто что смотрел — но здесь векторы выучены ранжированием, а не реконструкцией.
обучение и проекция факторов…
Теория простым языком
BPR — это та же matrix factorization (вектор пользователя · вектор фильма = интерес), но обученная по-другому. Вместо того чтобы угадывать число (как ALS), BPR учится ранжировать: для каждого человека ставить фильмы, с которыми он взаимодействовал, выше случайных, с которыми не взаимодействовал.
Pointwise против pairwise
Прошлая модель (ALS) — pointwise: она смотрит на одну пару «человек–фильм» и подгоняет под неё значение — у нас это не звёзды, а preference/confidence (помним: implicit-ALS не предсказывает рейтинг). Но нам в рекомендациях не нужно точное значение — нужен правильный порядок списка. BPR заходит с этой стороны.
▸Почему rating prediction не хватило
Тройка, на которой учимся
Берём пользователя и два фильма: один он трогал (позитив), другой — нет (негатив). Хотим, чтобы модель давала позитиву балл выше негатива.
Что считаем «позитивом» на MovieLens
У MovieLens есть оценки (звёзды), а BPR — модель для implicit-фидбека («было/не было взаимодействие»). Мы трактуем рейтинги как implicit-сигнал: позитив = любой оценённый фильм (с любым рейтингом), негатив — случайный неоценённый. Это упрощение, но согласованное: MF (implicit-ALS) и item-CF у нас учатся на той же матрице всех взаимодействий, а порог «понравилось» (rating ≥ 4) — это фильтр на этапе оценки, единый для всех моделей.
Казалось бы, чище брать позитивом только понравившееся (rating ≥ 4). Но мы это замерили: так BPR теряет половину обучающих пар (≈48k вместо ≈100k) и на нашем маленьком датасете недоучивается — NDCG@10 падает с ~0.032 до ~0.022. Хороший урок: офлайн-интуиция «позитив = понравилось» проигрывает замеру — на скудных данных число пар важнее чистоты сигнала.
Заметь: «Бэмби» — слишком лёгкий негатив. Если человек любит sci-fi, а в негатив случайно попал детский мультфильм, модель и так без труда ставит позитив выше — и почти ничему не учится. Тонкому различию между похожими фильмами учат «трудные» негативы (hard negatives) — о них в слабых сторонах ниже.
Как именно «подкручивает»
Для тройки считаем разницу баллов . Если позитив уже выше негатива ( большой) — всё хорошо, почти не трогаем. Если перепутаны (разница отрицательная) — делаем сильный шаг, чтобы исправить порядок. «Силу» шага задаёт сигмоида.
инициализируем векторы P (люди) и Q (фильмы) случайно
повторяем N эпох:
для каждого взаимодействия (u, i): # i — позитив
j = случайный фильм, не виденный u # негатив
x = P[u]·Q[i] − P[u]·Q[j] # разница баллов
g = σ(−x) # вес: больше, если порядок перепутан
P[u] += lr · (g·(Q[i] − Q[j]) − λ·P[u])
Q[i] += lr · (g·P[u] − λ·Q[i])
Q[j] += lr · (−g·P[u] − λ·Q[j])
score(u, i) = P[u]·Q[i] # для рекомендаций▸Формула: BPR-OPT и градиент
Балл — скалярное произведение, как в MF: . Для тройки (i — позитив, j — негатив) определяют и максимизируют:
Это максимизация вероятности правильного порядка пар — по смыслу близко к оптимизации AUC / pairwise-ранжирования, но через гладкую дифференцируемую функцию (а не дискретную AUC напрямую) — с L2-регуляризацией на все векторы . Градиентный шаг для одной тройки использует вес :
где близок к 1, когда пара перепутана (большой шаг), и к 0, когда порядок уже верный (шаг почти нулевой).
Почему это интересно после MF
Та же модель, другой критерий обучения — и на ранговых метриках BPR обычно обходит ALS-реконструкцию: мы оптимизируем порядок (по сути AUC по всем парам), а не значение оценки. Оговорка: это не прямая оптимизация top-K — NDCG@10 «top-heavy» (важна верхушка), и под него точнее заточены WARP / LambdaRank; BPR — самый простой первый шаг.
На наших данных BPR по NDCG@10 надёжно обходит MF (ALS); а с popularity идёт примерно вровень — разница в пределах шума, на части запусков даже ниже (плюс помним про popularity bias при пороге «понравилось»). item-CF впереди всех. Это и есть мостик к «learning-to-rank» — так учат ранкеры в больших продовых системах.
Честная оговорка: датасет маленький, в тесте у каждого спрятан один фильм, а сам BPR обучается на случайных негативах — поэтому метрика шумная и от запуска к запуску немного гуляет. Мы фиксируем seed, так что число воспроизводимо, но не стоит читать «BPR выше MF на 0.008» как закон природы — это «обычно выше на этой задаче».
Сильные стороны
- Оптимизирует порядок (пары «позитив выше негатива»), а не значение оценки — ближе к ранговым метрикам.
- Работает с implicit-фидбеком (клики/просмотры), не нужны явные оценки.
- Лёгкая и быстрая: SGD по тройкам, та же простая модель MF.
Слабые стороны
- Стохастична: результат зависит от негативов, lr и числа эпох (легко переобучить).
- Случайные негативы часто «слишком лёгкие» — модель мало из них узнаёт.
- Холодный старт остаётся: новому пользователю/фильму вектор взять неоткуда.
⚠️ Что может пойти не так
- «Негатив» = не взаимодействовал, а не «не понравилось». Это допущение implicit-фидбека: непросмотренное могло бы зайти.
- Качество негативов решает многое: равномерные негативы слишком лёгкие; hard-negative и popularity-based сэмплинг учат лучше (но и риск сместить выдачу).
- Переобучение по эпохам: точность растёт, потом резко падает (мы видели обвал при слишком долгом обучении) — число эпох надо подбирать.
- Это ранжирование, а не вероятности: баллы BPR нельзя читать как «шанс клика», только сравнивать между собой.
🧠 Проверь себя: Что напрямую оптимизирует BPR?
Где встречается в жизни
Pairwise-обучение на тройках «лучше/хуже» — классика ранжирования: от выдачи товаров и ленты до retrieval-этапа в больших рекомендателях. BPR — самый простой представитель этого семейства, с которого удобно начинать.