Graph Contextualized Self-Attention Network for Session-based Recommendation
GC-SAN(IJCAI 2019)会话推荐:SR-GNN 抓局部转移加自注意力抓长距离意图,再用一个标量权重融合最后一击与全局意图。
会话推荐里你能看到的只是一小段匿名点击序列,没有用户画像、历史长期偏好或人口统计,所有信号都封装在这几次点击中。GC-SAN(IJCAI 2019)的思路很务实:把当时最强的两个想法直接叠起来——SR-GNN 的会话图捕捉局部转移结构,Transformer 的自注意力捕捉长距离意图,最后用一个标量权重把“当前点击”和“全局意图”线性融合。它本身不引入新机制,但作为一个基准,至今仍难以被同等参数量级的模型超越。
你将学到什么#
- 为什么会话推荐结构上比传统协同过滤难
- 一段点击序列如何变成一张有向加权图
- GGNN 单元:入/出邻居聚合 + GRU 门控
- 自注意力作为图上下文化嵌入之上的全局编码器
- 融合权重 $w$ :最后一击 vs 全局意图
- GC-SAN 适合什么场景,不适合什么场景
前置知识#
- 图神经网络的消息传递与 GRU 风格更新
- 自注意力(Q/K/V,缩放点积)
- 推荐评估指标 Recall@K、MRR@K
问题设定与困难来源#
记物品全集为 $V = \{v_1, v_2, \dots, v_{|V|}\}$ ,一次会话是一段按时间排好的点击序列 $s = (v_{s,1}, v_{s,2}, \dots, v_{s,n})$ 。任务是预测 $v_{s,n+1}$ ,通常对所有候选打分排序并报告 Recall@K、MRR@K。
为什么这件事比传统协同过滤更棘手:
- 没有长期画像:无法依赖稳定的用户嵌入或人口统计特征。
- 行为短而嘈杂:会话中可能夹杂探索性点击、误点和来回跳转。
- 长距离依赖:早期的“相机”点击,可能直到后面的“存储卡”出现时才显出关联。
- 重复转移:用户经常在几个相关物品之间反复跳转,单纯的序列模型容易浪费这种结构信息。
这四种压力相互制约。纯序列模型(如 RNN 或 Transformer)能处理顺序但将每一步都视为新 token;纯图模型(如 SR-GNN)能捕捉回环和重复,但要达到远距离点击需要多跳传播。GC-SAN 的设计正是将两者结合,避免单独使用任一方法时的缺陷。
GC-SAN 在已有工作中的位置#

在 GC-SAN 之前,常见的基线大致分为四类:
- 马尔可夫链:局部信号强,但缺乏全局视角。
- GRU4Rec:用 GRU 构建会话内的顺序依赖,超过几步后开始吃力。
- NARM、STAMP:基于注意力的序列模型,更好地处理长距离依赖,但忽略了会话内部天然存在的转移图结构。
- SR-GNN:将会话建成有向图,运行门控 GNN,能捕捉丰富的局部结构,但远距离意图依赖多跳传播,层数过多会导致过平滑。
GC-SAN 的贡献偏架构而非算法:保留 SR-GNN 的门控 GNN 作为局部编码器,再在上面叠一层 Transformer 风格的自注意力,让全局依赖不必靠图上多跳来达成。完整流程见图 1。
整个 pipeline 是严格串行的:点击序列 $\to$ 建图 $\to$ GGNN 传播 $\to$ 多层自注意力 $\to$ 融合“最后一击”与“全局意图” $\to$ 对所有物品打分。
会话图的构建#

对每条会话 $s$ 构造有向图 $G_s = (V_s, E_s)$ :
- 节点:会话中出现过的不同物品(去重)。
- 对相邻点击对 $(v_{s,i}, v_{s,i+1})$ 加一条有向边 $v_{s,i} \to v_{s,i+1}$ 。
- 同一条转移多次出现就累加权重(或者多重边后再归一化)。
这一步在用紧凑性换信息密度。同一物品在序列里出现两次会被压成一个节点,只有它们之间的转移承载重复。回环——比如 A $\to$ B $\to$ A——在图里直接显成环,而单纯的序列模型只会把它当作“A 出现了两次”。
$$ A^{out}_{ij} = \frac{w(v_i \to v_j)}{\sum_k w(v_i \to v_k)}, \quad A^{in}_{ij} = \frac{w(v_j \to v_i)}{\sum_k w(v_k \to v_i)}. $$图 2 走了一个完整例子。
注意点击序列 v1 v2 v3 v2 v4 收敛成 4 个节点的图,里面有一对 v2 <-> v3 回环,以及 v2 同时通向 v3 和 v4 的扇出。原序列长度 5,图只有 4 节点 4 条不同的边——这种压缩正是图视角的价值。
局部编码器:会话图上的 GGNN#

GC-SAN 直接复用 SR-GNN 的门控 GGNN 单元。每个节点带一个 $d$ 维嵌入 $h_i$ 。一步传播包含三件事。
$$a_i^{(t)} \;=\; A^{in}_{i,:} \, H^{(t-1)} W^{in} \;+\; A^{out}_{i,:} \, H^{(t-1)} W^{out} \;+\; b,$$其中 $H^{(t-1)} = [h_1^{(t-1)}, \dots, h_{|V_s|}^{(t-1)}]^\top$ 把会话内所有节点嵌入堆起来,$W^{in}, W^{out} \in \mathbb{R}^{d \times d}$ 都是可学习的。这两项分别表示“流向我的证据”和“我流向别人的证据”,对方向敏感的转移很重要。
$$ \begin{aligned} z_i^{(t)} &= \sigma(W_z a_i^{(t)} + U_z h_i^{(t-1)}), \\ r_i^{(t)} &= \sigma(W_r a_i^{(t)} + U_r h_i^{(t-1)}), \\ \tilde h_i^{(t)} &= \tanh\!\bigl(W_h a_i^{(t)} + U_h (r_i^{(t)} \odot h_i^{(t-1)})\bigr), \\ h_i^{(t)} &= (1 - z_i^{(t)}) \odot h_i^{(t-1)} \;+\; z_i^{(t)} \odot \tilde h_i^{(t)}. \end{aligned} $$更新门 $z$ 决定新图信号写进多少;重置门 $r$ 决定形成候选状态时要忘掉多少旧状态。图 3 在单个节点上把这一步可视化。
跑 $T$ 步传播之后(论文常用 $T=1$ ,有时 $T=2$ ),每个节点嵌入都吸收了它在图上的局部邻域。关键点:传播是在每条会话各自的图上做的,不是全局物品图,所以嵌入是会话条件化的。
实现细节:会话里物品会重复出现,所以实现里要维护一个 alias 映射,把序列位置映射到去重后的节点索引。GGNN 跑完之后,再把节点状态“散布”回序列位置上,才能丢进自注意力。这就是任何标准实现里
seq_hidden = hidden[alias_inputs]在做的事。
全局编码器:会话上的自注意力#

GGNN 在局部很强,但要触达远端点击需要多跳,而 GGNN 层数堆多了又会过平滑——节点嵌入互相塌陷。自注意力直接绕开这个问题:任何位置一步就能注意到任何其他位置。
$$F = \mathrm{softmax}\!\left(\frac{(E W^Q)(E W^K)^\top}{\sqrt{d}}\right)(E W^V),$$ $$E^{(1)} = \mathrm{ReLU}(F W_1 + b_1) W_2 + b_2 + F.$$叠 $k$ 层得到 $E^{(k)}$ ,也就是图上下文化之后的序列表示。图 4 展示了自注意力典型学到的模式,以及为什么 GGNN 单独做不到同样的连接。
热力图是示意性的,但模式是真实的:第一击“相机”会强烈注意到会话深处与之主题相关的物品(如存储卡、电池、相机包)——这种连接如果靠 GNN 走 4 跳,信号早就被平滑掉了。
融合:最后一击 vs 全局意图#
会话推荐几乎总是需要显式地混合两种信号:
- 当前兴趣 $h_t$ :最后点击物品的嵌入,通常是最强的短期预测信号。
- 全局意图 $a_t$ :自注意力栈最后一个位置的输出,整合了整段会话。
权重 $w$ 是一个超参(通常的甜区在 $w \in [0.4, 0.6]$ )。$w = 0$ 退化为只用最后一击;$w = 1$ 则完全丢掉强短期信号。图 5(b) 的扫描给出典型曲线——中间平坦、两端陡降。
训练与评估#

其中 $i$ 是正样本(被点击),$j$ 是采样的负样本。BPR 适合隐式反馈场景,它直接优化“用户对正样本的偏好高于负样本”这一相对关系,不需要绝对评分。
标准 benchmark 是 Yoochoose1/64 和 Diginetica,报 Recall@20 与 MRR@20。图 5 复现了论文里的对比格局。
从图中可以得出两点:
- GC-SAN 相对 SR-GNN 的提升是稳定的,但不算夸张(Recall 和 MRR 上大约 +1 到 +2 个点)。在图编码器之上添加自注意力的边际价值是真实的,但有上限。
- 融合权重曲线中间平缓、两端陡峭——这正是一个好的超参应有的特性:易于调整且不易出错。
工程实践要点#
alias 映射与批处理: 会话里有重复物品,要把序列位置映射到去重图上的节点索引。多条会话图一起 batch 是个非平凡的事:要么拼成块对角邻接矩阵,要么用支持批量图操作的库(PyG、RecBole-GNN)。
复杂度。
- GGNN:每条会话约 $\mathcal{O}(T \cdot |E_s| \cdot d)$ ,$T$ 是传播步数,$|E_s|$ 是会话图边数。
- 自注意力:每条会话 $\mathcal{O}(n^2 d + n d^2)$ ,关于会话长度 $n$ 是二次。会话推荐里 $n$ 通常很短(往往 $< 50$ ),二次代价完全可接受。
会改变行为的关键超参。
- 传播步数 $T$ :太小抓不到多跳转移;太大会过平滑。论文默认 $T = 1$ 。
- 自注意力层数 / 头数:层数多了容量大,但在 Diginetica 这种小数据集上容易过拟合。
- 融合权重 $w$ :控制“全局 vs 最后一击”。在验证集上扫一遍即可,最优点通常在 $0.5$ 附近且很平。
Padding 与 mask。 自注意力必须 mask 掉 padding 位置,否则梯度会泄漏到无意义的 token 上。这是移植代码时很容易悄无声息退化的一个点。
参考实现简版#
下面是 RecBole 风格的最简版本。GGNN 单元复用自 SR-GNN,其余的接线才是 GC-SAN 的真正贡献。
| |
几个值得注意的点:
- GGNN 单元是直接 import 复用而非重写,再次说明 GC-SAN 主要是接线创新。
gather_indexes在item_seq_len - 1位置取嵌入,拿到的是真正的最后一击,而不是被 padding 撑出的尾部。self.weight是 config 里读的固定标量。一个自然的延伸是让它输入相关(在 $h_t \oplus a_t$ 上接一个小 gating MLP),但论文没有展开。- 损失可以在 BPR 和全 softmax 交叉熵之间根据 $|V|$ 大小切换。
什么时候用 GC-SAN,什么时候不用#
适合:
- 会话内部有显著的转移结构(回环、重复、相关物品来回跳)。
- 会话长度短到中等,$\mathcal{O}(n^2)$ 自注意力代价可接受。
- 想要一个把图和序列信号都吃到的强 baseline,且不引入额外基础设施。
局限和风险:
- 自注意力代价随会话长度二次增长。会话特别长就要换线性注意力变体,或者把会话切块。
- 建图选择(边权、归一化方式)会悄悄影响结果,改动时记得在小验证集上对照。
- 如果物品 metadata 很关键(文本、图像、类目层级),纯 ID 版本的 GC-SAN 没把这些信息吃进去。可以在 GGNN 之前拼接侧信息嵌入,或者切到内容感知变体。
- 相对 SR-GNN 的提升真实但不大。如果上不起一层 self-attention,单独的 SR-GNN 也是个不错的起点。
实操总结#
GC-SAN 与其说是巧思,不如说是一份冷静的“两个都用”配方:
- GGNN 高效抓局部转移和重复——但要够远必须多跳,多跳就过平滑。
- 自注意力 一步抓长距离依赖——但把输入当扁平序列,看不到图结构。
- 用一个标量融合权重 $w$ 把“最后一击”和“全局意图”接起来,且甜区很平。
放到 2024 年看,GC-SAN 仍然是一个值得对标的 baseline:它能告诉你新模型是不是真的比“图编码器 + Transformer 块”更善于利用会话结构。如果不是,那就少花点算力,先弄清楚瓶颈在哪。
参考文献#
- Xu et al., “Graph Contextualized Self-Attention Network for Session-based Recommendation,” IJCAI 2019. 论文 PDF
- Wu et al., “Session-based Recommendation with Graph Neural Networks (SR-GNN),” AAAI 2019.
- Hidasi et al., “Session-based Recommendations with Recurrent Neural Networks (GRU4Rec),” ICLR 2016.
- Li et al., “Neural Attentive Session-based Recommendation (NARM),” CIKM 2017.
- Liu et al., “STAMP: Short-Term Attention/Memory Priority Model for Session-based Recommendation,” KDD 2018.