Нейросетевые подходы
~20 мин

Sequential RecSys

SASRec, BERT4Rec, GRU4Rec — учёт последовательности действий пользователя.

Sequential RecSys — когда порядок действий важнее набора

Загрузка интерактивного виджета...

Ты посмотрел «Игру престолов», потом «Ведьмака», потом «Властелина колец». Что дальше? Collaborative filtering видит набор — три фэнтези-фильма — и предложит ещё один фэнтези. Но sequential-модель видит тренд: ты двигаешься от сериалов к фильмам, от тёмного фэнтези к классическому. Может, «Хоббит»?

Разница между «человек купил кроссовки, шорты и футболку» и «человек купил кроссовки → шорты → футболку» — огромна. Во втором случае видно: он собирает спортивный комплект. Следующее — спортивная сумка. Порядок = намерение. Sequential-модели ловят паттерны поведения, которые невозможно увидеть в статичном наборе взаимодействий.

Большая картина: как работают sequential-модели

Вход — упорядоченная последовательность действий пользователя: [item₁, item₂, ..., itemₙ]. Это могут быть просмотры, клики, покупки — любые события с временнóй меткой. Модель читает эту последовательность и предсказывает следующий айтем — itemₙ₊₁.

Полный pipeline: 1. Embedding — каждый item_id превращается в вектор (обучаемая таблица, как word2vec для товаров) 2. Positional encoding — добавляем информацию о позиции в последовательности (недавние действия важнее давних) 3. Sequence encoder — GRU, self-attention или bidirectional attention — модель «читает» историю и строит контекстное представление 4. Prediction — скалярное произведение выхода модели с эмбеддингами всех айтемов каталога → ранжированный список рекомендаций

Три ключевых архитектуры определили развитие этой области: GRU4Rec (2015) — рекуррентная сеть, SASRec (2018) — causal self-attention (как GPT), BERT4Rec (2019) — bidirectional attention (как BERT). Каждая следующая решала проблемы предыдущей.

GRU4Rec — первый нейросетевой подход

GRU4Rec (Hidasi et al., 2015) — статья, которая запустила направление. До неё сессионные рекомендации строились на item-based kNN или марковских цепях. GRU4Rec показал: рекуррентная сеть может моделировать сессию целиком.

Идея прямолинейная: каждое действие пользователя (клик, просмотр) — вход GRU-ячейки. Скрытое состояние h_t после t шагов — «понимание» текущей сессии. На каждом шаге предсказываем следующий айтем. Обучение через BPR loss (Bayesian Personalized Ranking): модель учится ставить положительный айтем выше случайного отрицательного.

BPR loss: i — положительный айтем, j — случайный негативный, σ — сигмоида. Модель учится: score позитива > score негатива.

Проблемы GRU4Rec унаследованы от RNN: • Затухание на длинных последовательностях — после 50-100 шагов ранние действия почти не влияют на предсказание • Последовательное обучение — нельзя параллелить по позициям; GPU простаивает • Нет прямого доступа к прошлому — чтобы «вспомнить» первый айтем, информация должна пройти через все скрытые состояния

Но для коротких сессий (5-20 действий) GRU4Rec работает хорошо и остаётся сильным бейзлайном. Если сессии короткие и инфраструктура простая — это разумный выбор.

SASRec — self-attention для последовательностей

SASRec: последовательность айтемов → self-attention → предсказание следующего
SASRec: каждый айтем «внимает» на все предыдущие через каузальную маску. Архитектурно — Transformer decoder (как GPT).

SASRec (Kang & McAuley, 2018) заменил рекуррентность на self-attention. Каждый айтем в истории напрямую «смотрит» на все предыдущие и решает, какие из них важны для текущего предсказания. Нет цепочки скрытых состояний — прямой доступ к любой позиции.

Ключевой элемент — каузальная маска (causal mask). Позиция i видит только позиции 1..i, но не i+1, i+2, ... Точно как в GPT: модель не может «подглядывать» в будущее. Без маски модель выучит тривиальное решение — скопировать следующий айтем из входа.

Self-attention с каузальной маской M. M[i][j] = 0 для j ≤ i (разрешено), M[i][j] = -∞ для j > i (запрещено). d — размерность.

Почему SASRec лучше GRU4Rec:Параллелизм — все позиции обрабатываются одновременно (GRU — строго последовательно) • Прямой доступ к прошлому — позиция 100 напрямую «видит» позицию 1 через attention (в GRU информация должна пройти 99 скрытых состояний) • Адаптивное внимание — модель сама решает, что важно: вчерашний просмотр или покупка месяц назад

На практике SASRec удивительно компактный: 2 слоя, 1-2 головы attention, размерность 64 — и он уже бьёт GRU4Rec. Это стандартный Transformer decoder (как GPT), только вместо токенов — айтемы каталога.

BERT4Rec — bidirectional подход

BERT4Rec (Sun et al., 2019) перенёс идею BERT в рекомендации. Вместо каузальной маски — bidirectional attention: каждая позиция видит контекст и слева, и справа. Обучение через Cloze task (маскирование): случайные 15-20% айтемов заменяются на токен [MASK], и модель предсказывает, что было на их месте.

Представь последовательность: «Интерстеллар» → [MASK] → «Гравитация» → «Марсианин». SASRec для предсказания замаскированной позиции видит только «Интерстеллар» слева. BERT4Rec видит и «Интерстеллар» слева, и «Гравитацию» + «Марсианина» справа — полный контекст. По аналогии с NLP: GPT читает текст слева направо, BERT — в обе стороны.

Проблема BERT4Rec — train-test mismatch. При обучении маски стоят в случайных позициях. При инференсе нужно добавить [MASK] в конец последовательности, чтобы «спросить» модель о следующем айтеме. Но модель никогда не видела [MASK] именно в последней позиции — это создаёт расхождение между обучением и применением.

SASRec vs BERT4Rec — что выбрать

Сравнение подходов

SASRec (causal, как GPT): • Видит только прошлое (позиции слева) • Обучается через next-item prediction • Inference простой: история → модель → предсказание • Нет train-test mismatch BERT4Rec (bidirectional, как BERT): • Видит контекст с обеих сторон • Обучается через маскирование случайных позиций • Inference: нужно добавить [MASK] в конец • Train-test mismatch из-за позиции [MASK]

На практике SASRec чаще побеждает — и вот почему. Next-item prediction точно соответствует задаче: мы хотим предсказать следующий айтем. Маскирование в BERT4Rec — это прокси-задача, которая не совпадает с целевой. Плюс SASRec стабильнее в обучении и проще в деплое.

BERT4Rec стоит пробовать, когда контекст «вокруг» пропуска важен: например, пользователь вернулся к старым интересам, и правая часть последовательности помогает понять, что изменилось. Но для большинства сценариев SASRec — базовый выбор.

Практика: SASRec на PyTorch

Разберём, что конкретно обучается в SASRec. Это частый вопрос на собесе — «а что именно внутри?»

import torch
import torch.nn as nn

class SASRec(nn.Module):
    def __init__(self, n_items, d=64, max_len=50, n_heads=2, n_layers=2):
        super().__init__()
        # 1. Item embedding: (n_items+1, d) — вектор каждого товара
        self.item_emb = nn.Embedding(n_items + 1, d, padding_idx=0)
        # 2. Positional embedding: (max_len, d) — кодирует позицию в сессии
        self.pos_emb = nn.Embedding(max_len, d)
        # 3. Transformer layers: Q, K, V + FFN в каждом слое
        layer = nn.TransformerEncoderLayer(
            d, n_heads, dim_feedforward=d * 4, batch_first=True
        )
        self.encoder = nn.TransformerEncoder(layer, n_layers)

    def forward(self, seq):  # seq: (batch, seq_len) — последовательность item_id
        positions = torch.arange(seq.size(1), device=seq.device)
        x = self.item_emb(seq) + self.pos_emb(positions)  # (batch, seq_len, d)
        # Каузальная маска: позиция i не видит позиции j > i
        mask = nn.Transformer.generate_square_subsequent_mask(seq.size(1))
        out = self.encoder(x, mask=mask.to(x.device))  # (batch, seq_len, d)
        # Предсказание: dot product с той же embedding matrix
        logits = out @ self.item_emb.weight.T  # (batch, seq_len, n_items+1)
        return logits

Что обучаемое:Item embedding matrix (n_items × d) — вектор каждого товара. Используется и на входе (превращает ID в вектор), и на выходе (скалярное произведение для предсказания). Единое пространство — экономит параметры. • Positional embeddings (max_len × d) — кодируют «давность» действия. Модель учится, что недавние действия обычно важнее давних. • Self-attention веса — Q, K, V матрицы в каждом слое и каждой голове. • FFN веса — feed-forward сеть после attention в каждом слое.

Training vs Inference

Training: модель получает последовательность [item₁, ..., itemₙ] целиком. Каузальная маска не даёт позиции i видеть будущее. На каждом шаге loss: предсказанный item vs реальный следующий (teacher forcing). Inference: берём последние N действий пользователя → прогоняем через модель → берём выход последней позиции → dot product с эмбеддингами всех айтемов → сортируем по убыванию → top-K = рекомендации.

Loss: softmax на миллионах айтемов

Модель выдаёт logits для каждого айтема каталога. Идеальный loss — softmax cross-entropy по всему каталогу. Но если айтемов миллион, это O(|V|) на каждый пример — нереально по памяти.

Full softmax: экспонента для КАЖДОГО айтема. При |V| = 10M — невозможно.

Sampled softmax — практичное решение. Берём target + K случайных negative samples (обычно K = 100-10000). Считаем softmax только по ним. Вместо O(|V|) получаем O(K).

Но есть подвох: log-Q коррекция. Негативы сэмплируются пропорционально популярности — «Coca-Cola» попадёт в негативы почти в каждом батче. Модель начинает занижать score популярных айтемов. Log-Q correction компенсирует это: вычитаем log(q(j)) из logit, где q(j) — вероятность сэмплирования. Без этой коррекции рекомендации будут странными.

Log-Q correction: убираем bias от неравномерного сэмплирования негативов.

В продакшене: двухэтапная система

Sequential-модель в продакшене не работает в одиночку. Стандартная схема: 1. Candidate generation — SASRec/BERT4Rec выбирает top-500 из миллионов. Быстро: один forward pass + approximate nearest neighbors. 2. Ranking — тяжёлая модель с дополнительными фичами (цена, CTR, свежесть, контекст) переранжирует top-500 в финальный top-20. Sequential-модель — это первый этап. Она отвечает за «что вообще показать?», а не за финальный порядок.

🎯 На собеседовании

Junior

Зачем sequential-модели, если есть CF? CF видит набор взаимодействий, а не их порядок. Sequential-модели ловят тренды и паттерны поведения: «кроссовки → шорты → футболка» = спортивный комплект. • GRU4Rec vs SASRec — в чём разница? GRU4Rec — рекуррентная сеть, обрабатывает последовательно, забывает начало. SASRec — self-attention, параллельный, прямой доступ к любой позиции. • Что такое каузальная маска? Запрет «заглядывать в будущее»: позиция i видит только позиции 1..i. Как в GPT.

Middle

SASRec vs BERT4Rec — когда что? SASRec — causal (GPT-style), проще inference, нет train-test mismatch. BERT4Rec — bidirectional, мощнее теоретически, но [MASK]-токен при инференсе создаёт mismatch. SASRec — базовый выбор. • Что обучается в SASRec? Item embeddings + positional embeddings + attention (Q/K/V) + FFN. Item embedding shared между входом и выходом. • Как считать loss на 10M товаров? Sampled softmax + log-Q correction. Full softmax нереально. In-batch negatives — ещё эффективнее. • Training vs Inference? Training: teacher forcing, каузальная маска. Inference: последние N действий → forward pass → dot product со всеми embeddings → top-K.

Senior

Почему SASRec на практике часто лучше BERT4Rec? Next-item prediction точно соответствует целевой задаче. Cloze task — прокси-задача. Train-test mismatch из-за позиции [MASK]. SASRec стабильнее в обучении. • Как бороться с popularity bias в sampled softmax? Log-Q correction: z_j = z_j - log(q(j)). Без коррекции модель занижает score популярных айтемов, которые постоянно в негативах. • Как масштабировать sequential-модель на триллионы событий? Шардинг embedding table (> 100M айтемов не помещается в GPU), mixed-precision training, curriculum learning (сначала короткие сессии, потом длинные). Feature hashing для редких айтемов. • Foundation model для RecSys — зачем? Одна модель вместо зоопарка под каждую задачу. Обучается на всех типах взаимодействий, файнтюнится под конкретный сценарий. Netflix и Яндекс (ARGUS) идут в этом направлении.

Собираем всё вместе

Sequential RecSys — это переход от «что пользователю нравится» к «что пользователь делает прямо сейчас». Три архитектуры определили область: GRU4Rec (RNN, простой бейзлайн для коротких сессий), SASRec (causal self-attention, основной выбор на практике), BERT4Rec (bidirectional, мощнее теоретически, но сложнее в деплое).

Если запомнить одну вещь: SASRec — это GPT для рекомендаций. Та же архитектура (Transformer decoder), та же идея (предсказание следующего элемента), та же каузальная маска. Если ты понимаешь GPT — ты понимаешь SASRec.

Дальше на роадмапе: Multi-Stage Ranking покажет, как sequential-модель встраивается в полный production pipeline — от candidate generation до финального ранжирования.