session-based 的概念:
session是服务器端用来记录识别用户的一种机制。典型的场景比如购物车,服务端为特定的对象创建了特定的Session,用于标识这个对象,并且跟踪用户的浏览点击行为。这里可以将其理解为具有时序关系的一些记录序列。
可以理解为从进入某个app直到退出的全过程中,根据用户的行为变化所发生的推荐,此时session是从进入app到退出app的全过程;也可以理解为根据用户某段时间内的行为序列发生的推荐,此时session不一定是从进入app到退出的全过程,比如airbnb的论文中,只要前后两次的点击不超过30min,都算作同一个session。
基于物品的协同过滤 指根据用户对物品的行为数据来计算物品与物品之间的相似度,为用户推荐喜欢的物品相似的物品,例如:如果一个用户喜欢物品A的同时也喜欢物品B那么就认为物品A与物品B相似;
基于用户的协同过滤 指通过物品对用户的行为数据来计算用户与用户的相似度,为用户推荐相似用户喜欢的物品。协同过滤在能够进行高质量推荐之前需要大量的历史数据,面临着冷启动问题。
实时推荐系统常常面临只能依靠短序列建模而不是长的用户历史数据的问题。
在实际中通常使用item-to-item推荐方法(建立item to item的相似度矩阵)来解决,比如当用户在一个session中点击了某一个物品时,基于相似度矩阵得到相似的物品推荐给用户。这种方法简单有效,并被广泛应用,但是这种推荐方法只考虑了用户的最后一次点击,实际上忽略了过去的点击(clicks)信息。
另一种方法是利用马尔可夫决策过程(MDPs)进行推荐,
本文提出对整个序列建模可以提供更精准的推荐。其主要学习的是状态转移概率,即点击了物品A之后,下一次点击的物品是B的概率,并基于这个状态转移概率进行推荐。这样的缺陷主要是随着物品的增加,建模用户所有的可能的点击序列变得难以管控。
General Factorization Framework (GFF) 方法用事件(event)之和来建模会话(session),用item本身的特征表达以及item作为会话的一部分的表达的平均值作为潜在表达(latent representation)。但是这种方法没有考虑会话中的事件的顺序信息。
本文提出的模型是基于RNN的方法来进行基于会话的推荐,我们可以称之为GRU4Rec模型。
模型结构如下:
输入: 依次输入一个session的点击序列 x = [ x 1 , x 2 . . . x r − 1 , x r ] , 1 ≤ r ≤ n x=[x_1, x_2 ... x_{r-1}, x_r], 1\leq r \leq n x=[x1,x2...xr−1,xr],1≤r≤n。
序列中的每一个 x i , x i ∈ x x_i,x_i\in x xi,xi∈x 表示成独热编码,随后通过embedding层压缩为低维连续向量作为 GRU 单元的输入。
输出: 预测每一个item被点击的预测概率 y = M ( x ) , w h e r e y = [ y 1 , y 2 . . . y m ] y=M(x),where y =[y_1, y_2...y_m] y=M(x),wherey=[y1,y2...ym],其中概率最大的 y j y_j yj为预测的下一个被点击的item。
GRU层: RNN效果不佳,LSTM同准确度下速度更慢
Embedding层和 Feedforward层: 可选项
GRU层数: 层数为1时效果最佳;会话跨越较短的时间框架;不需要多尺度建模。
为了提高模型训练的效率,修改训练策略使GRU适用于RecSys任务。
提出该trick的动机:
1)会话长度差异很大,例如 s e s s i o n 1 session_1 session1包含2个事件,而 s e s s i o n 2 session_2 session2可能包含100个事件;
2)捕捉会话随时间的演变过程。
具体策略:
如图所示,Session1的长度为4,Session2的长度为2,Session3的长度为6,Session4的长度为2,Session5的长度为3。假设Batch-Size为3,那么我们首先用前三个Session进行训练,不过当训练到第三个物品时,Session2已经结束了,那么我们便将Session4来接替上,不过这里要注意将GRU中的状态重新初始化。
提出该trick的动机:
1)item的数量通常很多(十万甚至上百万个);
2)训练规模=事件数 * hidden layer层数 * 输出维度(等于item总数),为 O ( N E H N I ) O(N_EHN_I) O(NEHNI)
3)item样本和用户行为的变化很快,需要频繁地训练模型。
具体策略:
图中 i 1 i_1 i1为对应某输入的正样本, i 5 i_5 i5和 i 8 i_8 i8则为负样本。
动机:
方法:
此处引用文哥的一段原文:
好了,最后我们来讨论一下我阅读本文时最为疑虑的地方吧,为什么使用pair-wise的损失函数,要比point-wise的损失函数更好呢?这主要还是看场景吧。比如在电商领域、外卖点餐的时候,我们可能很多东西都喜欢,但是只会挑选一个最喜欢的物品进行点击或者购买。这种情况下并不是一个非黑即白的classification问题,只是说相对于某个物品,我们更喜欢另一个物品,这时候更多的是体现用户对于不同物品的一个相对偏好关系,此时使用pair-wise的损失函数效果可能会好一点。在广告领域,一般情况下用户只会展示一个广告,用户点击了就是点击了,没点击就是没点击,我们可以把它当作非黑即白的classification问题,使用point-wise的损失函数就可以了。
不过还是要提一点,相对于使用point-wise的损失函数,使用pair-wise的损失函数,我们需要采集更多的数据,如果在数据量不是十分充足的情况下,point-wise的损失函数也许是更合适的选择。
论文下载地址为:http://arxiv.org/abs/1511.06939
文哥的学习日记:https://www.jianshu.com/p/9a4b3791fda2
原文代码地址:https://github.com/hidasib/GRU4Rec
tf参考代码地址:https://github.com/princewen/tensorflow_practice/tree/master/recommendation/Basic-SessionBasedRNN-Demo
python2代码地址:https://github.com/Songweiping/GRU4Rec_TensorFlow