《Session-Based Recommendations With Recurrent Neural Networks》
有些电商推荐系统、新闻网站、媒体网站不会跟踪用户 ID
。虽然 cookie
和浏览器指纹技术可以提供一些程度上的用户识别,但是这些技术通常不可靠并且容易引发隐私问题。即使可以跟踪用户 ID
,但是用户在一些小型电商网站上可能只有一两个会话(session
),并且在某些领域(如分类网站classified site
)中,用户的行为通常表现出session-based
的特征。因此,同一个用户的每个会话应该独立地处理。
在中国的互联网早期,存在大量的、可以匿名访问的网站或
APP
,因此不会跟踪用户ID
。随着移动互联网浪潮的兴起,中国互联网大多数网站或APP
需要登录才能访问,典型的例子是淘宝,因此可以跟踪到用户ID
。即使能够跟踪到用户ID
,我们也无法保证操作手机的是同一个用户。
目前大多数电商的session-based
的推荐系统都使用相对简单的方法,如 item-to-item
相似性、item
共现、转移概率等等。虽然有效,但是这些方法仅考虑用户的最后一次行为,而忽略了用户的历史行为。
此外,推荐系统中最常用的是因子模型(factor model
)和邻域方法(neighborhood method
)。
因子模型将稀疏的 user-item
交互矩阵分解为一组 user
潜在因子向量和 item
潜在因子向量来完成矩阵补全问题(例如用潜在因子向量的内积)。由于无法跟踪用户 ID
(导致无法获取用户历史交互的 item
),所以因子模型很难应用于session-based
的推荐。
邻域方法依赖于会话内的 item
共现,因此被广泛应用于 session-based
推荐中。
RNN
在翻译、对话建模、图像标注(image captioning
)等领域取得了显著成功,但是很少有人将其用于推荐领域。在论文 《Session-Based Recommendations With Recurrent Neural Networks》
中,作者认为 RNN
可以应用于 session-based
推荐并取得了显著效果。作者处理了在建模这类稀疏序列数据进行时出现的问题,并通过引入适当的、新的 ranking loss function
来使得 RNN
模型适应推荐 setting
。
session-based
推荐问题在建模方面与一些 NLP-related
问题相似,比如它们都处理序列数据。在 session-based
推荐中,我们可以将用户在会话中的第一个交互 item
作为 RNN
的初始输入,然后我们根据这个初始输入来 query
模型从而获得推荐。然后,用户的每次后续交互都会产生一个推荐,这个推荐取决于所有先前的、同一个会话内的交互。 item
集合可能是数万甚至数十万,另外交互行为可能非常频繁,因此训练时间和可扩展性非常重要。
和大多数信息检索和推荐 setting
一样,论文主要关注于建模用户最感兴趣的 top item
,为此论文使用 ranking loss function
来训练 RNN
。
相关工作:
session-based
推荐:推荐系统领域的大部分工作都集中在当 user ID
可用并且可以获取用户画像时的模型上。这种情况下,矩阵分解模型、邻域模型在文献中占据主导地位。session-based
推荐中采用的主要方法之一是 item-to-item
推荐,此时 item-to-item
相似度矩阵是从可用的会话数据中预计算而来,也就是说,在同一个会话中共现的 item
被认为是相似的。这种方法虽然简单但是已被证明有效,并被广泛采用。但是,该方法仅考虑用户的最近一次行为,忽略了用户历史行为信息。
session-based
推荐的另一个稍微不同的方法是马尔科夫决策过程(Markov Decision Processes: MDP
)。MDP
是序列随机决策问题的模型,最简单的 MDP
本质上是一阶马尔科夫链,其中 next
推荐可以简单地根据 item
之间的转移概率来计算。 在 session-based
推荐中应用马尔科夫链的主要问题是:当试图包含所有可能的用户行为序列时,状态空间很快变得无法管理。
通用分解框架(General Factorization Framework: GFM
)的扩展版本能够使用会话数据来推荐。该方法通过事件(event
)的 sum
来建模会话,并对 item
使用两种潜在 representation
:一种代表 item
本身、另一种代表 item
是会话的一部分。然后将会话中所有 item
的第二种 representation
取平均从而作为会话的 representation
。但是,该方法不考虑会话中的行为顺序。
推荐系统中的深度学习:在推荐系统中最早应用深度学习的相关方法之一是 《Restricted boltzmann machines for collaborative filtering》
,它使用 RBM
来对 user-item
交互进行建模并执行推荐。此外,深度模型也用于从音乐或图像等非结构化内容中提取特征,然后与更传统的协同过滤模型一起使用,如 《Deep content-based music recommendation》
。最近,《Collaborative deep learning for recommender systems》
引入了一种更通用的方法,该方法使用深度网络从任何类型的 item
中抽取通用的内容特征,然后将这些特征集成到标准的协同过滤中从而提高推荐性能。这些方法在缺乏足够 user-item
交互的 setting
中特别有用 。
标准的 RNN
:
其中:hidden state
),sigmoid
函数)。
Gated Recurrent Unit: GRU
是一个更精细的 RNN
模型,旨在处理梯度消失问题。GRU gates
本质上是学习何时更新单元的隐状态、以及更新多少:
其中:update gate
,reset gate
,sigmoid
激活函数, candidate activation
)。
我们在模型中使用 GRU-based RNN
来进行 session-based
推荐。网络的输入是每个时刻会话的实际状态(actual state
),输出是会话中下一个事件的 item
。
会话的状态可以是最新的事件(即当前事件)。此时使用 OneHot
编码,即输入向量的长度为 item
为 1
而其它位置为 0
,其中 item
集合大小。
会话的状态也可以是截止到当前的所有事件(即累计事件)。此时对每个事件的 OneHot
编码进行加权和,权重根据事件发生时刻距离当前时间间隔进行衰减。为了稳定性,我们对输入向量进行归一化。
我们希望这种累计事件的方式会有所帮助,因为它增强了记忆效应(memory effect
)。但是实验发现,这种方式并不会带来额外的准确率增益。这毫不奇怪,因为 GRU
像 LSTM
一样,同时具有长期记忆和短期记忆。
我们尝试添加一个额外的 embedding layer
,但是发现 OneHot
编码总是表现得更好。
如果没有
embedding layer
,则分别充当不同的 embedding
矩阵; 如果添加额外的embedding layer
,则表示在已有的 embedding
矩阵之上的投影矩阵。
网络的核心是 GRU layer
。可以使用多层 GRU layer
,其中前一层的 hidden state
是下一层 的输入。最后一个 GRU layer
和输出层之间添加了额外的前馈层( Feedforward layer
)(但是实验发现增加这个额外的前馈层对于效果提升没有帮助)。输出是每个 item
在当前会话中成为 next
的可能性。input
层也可以选择连接到网络中更深的 GRU layer
,因为我们发现这可以提高性能。
完整的架构如下图所示,该架构描述了事件时间序列中单个事件的 representation
。
图中包含了
embedding layer
和feedforward layer
,但是根据实验描述,这两个layer
是无益的,需要移除。
由于推荐系统不是 RNN
的主要应用领域,我们修改了基础网络从而更好地适应任务。我们还考虑了实际场景,以便我们的解决方案可以应用于现场环境。
session-parallel mini-batch
:用于 NLP
任务的 RNN
通常使用 in-sequence mini-batch
。例如,通常在句子的单词上使用滑动窗口,并将这些窗口片段彼此相邻从而形成 mini-batch
。这不适合于我们的任务,因为:
首先,会话之间长度的差异非常大(远远大于 NLP
中的句子)。一些会话仅包含一到两个事件,而另一些会话可能包含超过数百个事件。
其次,我们的目标是捕获会话如何随时间的演变,因此将会话分解为片段是没有意义的。
因此,我们使用 session-parallel mini-batch
,如下图所示。具体而言:
首先,我们将所有会话进行排序。
注意,这里是以会话为基本单元进行排序,而不是对会话内的事件进行排序。
然后,我们使用前 X
个会话的第一个事件来构成第一个 mini-batch
的输入(所需的输出是这些会话的第二个事件)。第二个mini-batch
是由这些会话的第二个事件形成的,依次类推。
如果任何一个会话结束了,则下一个可用的会话将填补该结束会话的位置。假设会话之间是相互独立的,因此我们在发生这种会话切换的时候重置对应的 hidden state
。
这种训练方式可以支持任意长度的
session
,而无需将session
进行长度限制(比如截断为固定长度)。
sampling on the output
:当 item
集合规模太大时,在每个 step
计算每个 item
在当前会话中成为 next
的可能性是不现实的。因此我们需要负采样技术。
我们根据 item
的流行程度进行负采样: item
越流行,那么用户就越可能知道它,那么用户未对该 item
交互则意味着用户更有可能不喜欢该流行 item
。
我们没有为每个训练样本生成单独的负采样,而是使用来自 mini-batch
的其它训练样本的 item
作为负采样。这种方法的好处是我们可以减少计算时间、降低代码复杂性、并且也易于实现。同时,该方法也是一种基于流行程度的采样,因为一个 item
出现在 mini-batch
中的可能性与其流行程度成正比。
ranking loss
:推荐系统的核心是 item
的 relevance-based ranking
。尽管该任务也可以解释为分类任务,但是 learning-to-rank
方法通常优于其它方法。ranking
可以是 point-wise
、pair-wise
、或者 list-wise
的。
point-wise ranking
彼此独立地估计 item
的 score
或者 ranking
,并且损失函数的定义方式使得相关的 item
的排名应该靠前。
pair-wise ranking
比较一个 positive item
和一个 negative item
组成的 ranking pair
对,损失函数强制 positive item
的排名应该比 negative item
的排名更靠前。
list-wise ranking
使用所有 item
的 score
或 ranking
,并将它们与完美排序进行比较。它通常在计算上代价太大,因此不经常使用。
此外,如果仅有一个相关的 item
(例如我们这里的例子),list-wise ranking
可以通过 pair-wise ranking
来解决。我们在我们的解决方案中包含了几个 point-wise ranking loss
和 pair-wise ranking loss
。我们发现模型的 point-wise ranking loss
不太稳定,而 pair-wise ranking loss
表现良好,最终我们使用以下两个 pair-wise ranking loss
:
BPR
:贝叶斯个性化排名(Bayesian Personalized Ranking: BPR
)是一种矩阵分解方法,它使用 pair-wise ranking loss
。它比较一个 positive item
和一个负采样的 negative item
的 score
。这里,我们将 positive item
的 score
和几个负采样的 negative item
的 score
进行比较,并将它们的均值作为损失。具体而言,pair-wise ranking loss
为:
其中:sigmoid
函数,s
中 item
score
,positive item
,negative item
。
TOP1
:这个 ranking loss
是我们为这项任务而设计的,它是 relevant item
的相对排名(relative rank
)的正则化近似。relevant item
其中 sigmoid
函数来代替示性函数,优化这个相对排名将会修改 parameter
从而使得 item
score
会很高。
然而,这个目标函数不稳定,因为某些 positive item
也会扮演负样本角色,因此 score
往往会变得越来越高。
为避免这种情况,我们希望强制 negative item
的 score
在零左右,这是negative item
的 score
的自然预期。为此,我们在损失函数中添加了一个正则化项。重要的是,这一项与相对排名在同一取值区间并且作用与其相似。最终的损失函数为:
.
数据集:
RecSys Challenge 2015
数据集(RSC15
):该数据集包含电商网站上的click-streams
,其中某些stream
以购买事件而结束。我们仅保留点击事件,并且过滤掉长度为 1
的会话。我们使用前 6
个月的数据进行训练,使用第二天的会话进行测试。每个会话要么是训练集要么是测试集,我们不会在会话中拆分数据。
由于协同过滤方法的性质,我们从测试集中过滤掉训练期间 unseen
的 item
,并且删除测试集中长度为 1
的会话。
VIDEO
数据集:包含某个视频服务平台收集的视频观看事件。预处理步骤如前所述,但是也过滤掉了很长的会话,因为这些会话可能是由机器人生成的。训练数据集包含最后一天除外的所有数据,测试数据集包含最后一天的数据。
评估方式:通过 one-by-one
提供会话的事件并检查下一个事件的 item
排名来完成评估。GRU
的隐状态在会话结束之后重置为零。 item
按照 score
降序排名。
对于 RSC15
数据集,我们对训练集中出现的 37483
个 item
进行排名。对于 VIDEO
数据集,因为 item
集合太大因此我们将 next item
和最流行的 30000
个 item
进行排名。这种评估的影响可以忽略不计,因为冷门的 item
通常 score
很低。此外,基于流行度的预过滤在实际推荐系统中很常见。
recall@20
:在 top 20
排名的 item
中,召回的 next item
占所有 next item
的比例。
召回不考虑 item
的实际排名,只要它位于 top N
的位置。这很好地建模了某些实际场景,其中绝对顺序无关紧要。此外,召回率通常还与重要的在线指标密切相关,如点击率 CTR
。
MRR@20
:Mean Reciprocal Rank: MRR
是 next item
的排名的倒数的均值。如果排名在 20
之后则排名的倒数置为零。
MRR
考虑 item
的排名,这在推荐顺序很重要的场景下很重要(如排名靠后的 item
仅在滚动屏幕之后才可见)。
baseline
方法:
POP
:流行度的 predictor
,它总是推荐训练集中的流行 item
。尽管简单,但是它通常是某些领域的强大 baseline
。
S-POP
:推荐当前会话中最流行的 item
(而不是全局最流行的 item
)。该 baseline
在具有高重复性的领域中很强。
Item-KNN
:基于 item
相似性来推荐和当前 item
最相似的 item
。相似度定义为
此外,我们还使用正则化从而避免冷门 item
的偶然发生导致的高度相似性。
该 baseline
是实际系统中最常见的 item-to-item
解决方案之一。尽管简单,它通常也是一个强大的 baseline
。
BPR-MF
:它通过 SGD
优化 pair-wise ranking
目标函数,是最常用的矩阵分解方法之一。矩阵分解无法直接应用于 session-based
推荐,因为新会话没有预先计算的特征向量。然而,我们可以通过使用会话中出现的 item
的特征向量的均值作为 user
特征向量来克服这个问题。换句话讲,我们对 next item
的特征向量、迄今为止会话中 item
特征向量的相似性进行平均。
这些 baseline
的效果如下表所示,其中 item-KNN
方法显著优于其它 baseline
方法。
参数配置:我们通过单独优化每个超参数来进行超参数调优,其中调优是在单独的验证集上进行的。然后我们在训练集和验证集上对网络进行重新训练,并最终在测试集上进行评估。下表给出了最佳的超参数。
此外还有一些探索如下:
权重矩阵从
我们评估了 rmsprop
和 adagrad
优化器,发现 adagrad
效果更好。
我们对 GRU
以外的其它 RNN
单元进行了简单的实验,发现常规的 RNN
单元和 LSTM
单元的效果都更差。
我们尝试了几种损失函数。
point-wise ranking loss
(如交叉熵和 MRR
)优化通常是不稳定的,即使添加正则化也是如此。我们认为:这是由于这种方法试图为 positive item
获得高分,而负样本的 negative push
太小。
另一方面,pair-wise ranking loss
表现良好。
我们检查了几种架构,发现:
单层 GRU
表现最好。我们假设这是由于会话的生命周期通常都很短,不需要表达不同分辨率的多个时间尺度。然而,确切原因尚不明确,需要进一步研究。
使用 item embedding
得到的效果稍差,因此我们仅使用 OneHot
编码。
此外,将会话的所有历史事件(而不是最新的事件)作为输入并不能带来额外的准确率增益。这毫不奇怪,因为 GRU
像 LSTM
一样,同时具有长期记忆和短期记忆。
在 GRU layer
之后添加额外的前馈层也没有帮助。
然而,增加 GRU layer
的大小提高了性能。
我们还发现使用 tanh
作为输出层的激活函数是有益的。
实验结果:下表给出了性能最佳的网络的结果(括号表示相对于 item-KNN
中的提升。)。具有 1000
个隐单元的 VIDEO
数据集的交叉熵在数值上不稳定,因此我们没有提供结果。结论:
即使隐单元数量为 100
,GRU-based
的方法在两个数据集上都显著优于 item-KNN
。增加隐单元数量进一步改善 pair-wise loss
的结果,但是降低了交叉熵损失(即 point-wise loss
)的准确率。
在隐单元数量为 100
时,尽管交叉熵损失给出了更好的结果,但是随着隐单元数量的增加,pair-wise loss
超过了该结果。
尽管增加隐单元数量会增加训练时间,但是我们发现 GPU
上从隐单元 100
提升到 1000
的代价不太昂贵。
交叉熵损失在数值上不稳定,因为网络独立地尝试增加 positive item
的 score
,而其它 item
的 negative push
相对较小。因此,我们建议使用上述两个 pair-wise loss
中的任何一个。