第3章基于显式反馈的物品排序 推荐系统有两个重要任务: 评分预测和物品排序。前者旨在拟合用户对物品的评分值,后者侧重根据用户对物品的预测偏好对候选物品进行排序。本章主要关注物品排序任务,因为它最后的输出是物品排序列表,与真实应用中的推荐问题更为匹配,可以直接用于向用户推荐他或她可能感兴趣的物品。 对于用户反馈数据类型,通常可以分为显式反馈数据和隐式反馈数据。显式反馈数据通过评分值来反映用户对物品的喜爱程度,隐式反馈数据则通常是点击、浏览等形式的单值数据。本章关注基于显式反馈的物品排序算法,因为它包含了比隐式反馈更精确的偏好信息。 图31CR问题示意图 3.1协同排序(CR)问题 本章研究的问题是协同排序(collaborative ranking,CR)问题,其定义如下: 给定n个用户、m个物品以及用户对物品的历史评分记录R={(u,i,rui)},其中rui表示用户u对物品i的评分。目标是为每个用户生成个性化的物品排序列表,如图31所示。需要注意的是,当向用户进行推荐时,候选物品通常是指该用户所有未交互的物品,即J\Ju,其中J表示物品集合,Ju表示用户u交互过的物品集合。本章所用到的符号和其说明如表31所示。 表31CR问题中的符号及其说明 符号说明 n用户数 m物品数 d潜在特征向量的维度 U={u}用户集合 J={i}物品集合 R={(u,i,rui)}训练集的评分记录集合 M 多类偏好集合 rui∈M 用户u对物品i的真实评分 r-ui rui归一化后的评分值 JPu 训练集中用户u购买过的物品集合 JEu 训练集中用户u浏览过的物品集合 Jru,r∈M 用户u评分为r的物品集合 r^ui 用户u对物品i的预测评分 Uu·∈R1×d 用户u的潜在特征向量 Vi·∈R1×d 物品i的潜在特征向量 Mri′·∈R1×d 物品i′的潜在特征向量(评分为r) bu∈R 用户u的偏置 bi∈R 物品i的偏置 λmod. HoToR(mod.)中的权衡参数 λnei. HoToR(nei.)中的权衡参数 ski 物品k和物品i的相似度 Ni物品i的邻居集合 3.2粗精迁移排序 迁移排序(transfer to rank,ToR)[1]是一种简单有效的算法,包含浏览和评分两个阶段。它首先使用用户的浏览行为数据进行全局偏好学习,然后利用评分行为数据来进一步优化候选物品列表。ToR模型虽然在一定程度上模拟了用户的购物过程,但忽略了用户在购买选择上的差异,也就是说,某些用户虽然给物品打了高分,但他们不一定会购买该物品。为了解决此问题,粗精迁移排序(coarsetofine transfer to rank,CoFiToR)[2]将用户购物过程进一步细分为三个阶段,即浏览阶段(E阶段)、评分阶段(S阶段)和购买阶段(P阶段),对应三个具体问题: ①用户是否会浏览一个物品?②用户浏览一个物品后,会给它评多少分?③用户最终是否会购买该物品? 3.2.1模型介绍 CoFiToR算法的结构示意图如图32所示。它包含浏览、评分和购买三个阶段,从粗粒度到细粒度以递进的方式来建模用户对物品的偏好,每个阶段都代表用户购物流程中的一个步骤。具体而言,浏览阶段旨在提取出用户最有可能会浏览的物品; 接着,在评分阶段,它通过预测用户对可能会浏览的物品的评分,来进一步优化浏览阶段得到的候选物品列表; 最后,在购买阶段,它通过学习用户对物品的购买偏好,从评分阶段得到的候选物品列表筛选出用户最有可能会购买的物品,从而组成最终的候选物品列表。可以看出,CoFiToR的三个阶段依次对用户的购物过程进行建模分析,具有递进关系。在不同阶段之间,它通过候选物品列表的形式共享和传递知识,并通过不断优化候选物品列表来提升推荐性能。 图32CoFiToR算法的模型结构示意图(见彩插) CoFiToR的优化问题可以表示如下: Prob(E,S,P|R)(31) 其中,E、S和P分别表示基于原始评分记录生成的用于模拟浏览、评分和购买行为的数据。为了解决上面的优化问题,CoFiToR将其转换成三个子问题,并得到包含三个阶段的解决方案: Prob(E|ΘE)→Prob(S|ΘS; LE)→Prob(P|ΘP; LS)(32) 其中,ΘE、ΘS和ΘP是要学习的模型参数; LE是浏览阶段输出的候选物品列表; LS是评分阶段输出的候选物品列表。下面,我们将详细描述CoFiToR模型的三个阶段。 1. 浏览阶段 浏览阶段的目标是找出用户最有可能浏览的物品。为此,CoFiToR将用户所有的评分记录R当作用户的浏览行为E(如图32所示),即E=(u,i)|(u,i,rui)∈R,目标函数的表示如下: ProbE|ΘE(33) 其中,ΘE为浏览阶段要学习的模型参数。 为了提取用户可能浏览的物品,CoFiToR在浏览阶段采用BPR算法[3]。BPR算法基于成对的偏好假设,将所有观测到的记录都当作正样本,将未观测到的记录当作负样本,通过建模用户对正样本和负样本的偏好之间的差异来对物品进行排序,其目标函数如下: minΘ∑u∈U∑i∈JPu∑j∈J\JPufuij(34) 其中,fuij=-lnσr^ui-r^uj+αu2Uu·2+αv2Vi·2+αv2Vj·2+βv2b2i+βv2b2j是定义在(u,i,j)三元组上的目标函数; σ(·)是sigmoid函数。用户u对物品i评分的预测公式如下: r^ui=Uu·VTi·+bi(35) 其中,Uu·和Vi·分别表示用户u和物品i的潜在特征; bi表示物品i的偏置信息。 对于协同排序问题,直接使用BPR算法可能会造成信息损失,因为它平等地对待所有评分记录,没有考虑用户对物品的评分信息。为了赋予高分的物品更多的权重,CoFiToR将物品的评分信息融入BPR的目标函数中,获得的目标函数如下: minΘ∑u∈U∑i∈JEur-ui∑j∈J\JEufuij(36) 其中,JEu是训练集中用户u浏览过的物品集合; r-ui=(2rui-1)/25是用户u对物品i的评分rui的归一化值[4],5代表最高的评分值。通过引入归一化评分r-ui,不仅增加了正负样本之间的差异,而且有助于区分不同分数的正样本。 通过利用修正的BPR算法,CoFiToR在浏览阶段获得了候选物品列表LE,该列表将作为评分阶段的输入,以继续对其进行优化。 2. 评分阶段 在浏览阶段得到用户可能浏览的物品后,下一步需要估计用户对物品的评分值,这也是CoFiToR在评分阶段的目标。具体来说,CoFiToR将用户对物品的评分记录R当作用户的评分行为S(如图32所示),即S=R,目标函数的表示如下: ProbS|ΘS; LE(37) 其中,ΘS是评分阶段要学习的模型参数; LE是浏览阶段得到的候选物品列表。 为了预测用户对物品的评分,在评分阶段,CoFiToR采用PMF算法[5]来学习用户和物品的潜在特征。具体来说,它将用户对物品的偏好建模成用户潜在特征向量和物品潜在特征向量的内积,目标函数如下: minΘ∑nu=1∑mi=1yui12rui-r^ui2+αu2Uu·2+αv2Vi·2(38) 预测公式为: r^ui=Uu·VTi·+bu+bi+μ(39) 其中,Uu·和Vi·分别表示用户u和物品i的潜在特征向量; bu和bi分别表示用户u和物品i的偏置信息; μ表示全局平均偏好。 在评分阶段,通过利用PMF算法,CoFiToR可以得到用户对物品的预测评分,然后它对浏览阶段得到的候选物品列表LE进行重排(reranking),同时移除排序靠后的物品,从而得到大小为NS(NS<NE)的新候选物品列表LS,该列表会作为购买阶段的输入以进一步对其进行优化。值得注意的是,在ToR[1]中,LS就是最终的候选物品列表,它会被直接推荐给用户,而CoFiToR会继续对其进行优化,这也是CoFiToR与ToR最主要的区别。 需要说明的是,如果利用PMF的预测结果对所有未评分的物品(而非浏览阶段得到的候选物品列表LE)进行排序,结果往往会非常差。主要有两个原因,首先,PMF在建模过程中没有利用未观测到的(用户,物品)对,即没有考虑大量的负反馈信息,这样不利于算法找出用户感兴趣的物品; 其次,最小化平方误差rui-r^ui2不能有效地区分比真值rui大一点和小一点的预测值r^ui的好坏,但预测值的相对大小在排序中是非常重要的,即存在建模目标与排序目标的不一致性。ToR和CoFiToR中的重排策略巧妙地规避了以上两个问题,且第二阶段的逐点偏好学习算法又能与第一阶段的成对偏好学习算法形成一定的互补。 3. 购买阶段 在评分阶段得到用户对物品的预测评分后,需要知道用户是否会购买该物品,这也是CoFiToR在购买阶段要解决的问题。为了提取用户最有可能会购买的物品,CoFiToR将评分记录R中用户对物品的评分为5的记录(5为分数的最大值)当作用户的购买行为(如图32所示),即P={(u,i)|(u,i,5)∈R},目标函数的表示如下: ProbP|ΘP; LS(310) 其中,ΘP是在购买阶段要学习的模型参数; LS是评分阶段得到的候选物品列表。 为了提取用户最有可能购买的物品,CoFiToR在购买阶段再次采用BPR算法,原因在于基于成对偏好学习的BPR与基于逐点偏好学习的PMF有较强的互补性,这也在ToR中得到了证实[1]。注意,购买阶段使用的是原始的BPR算法,即式(34),没有融入用户的评分信息,这与浏览阶段的不同。 在购买阶段,为了识别用户可能会购买的物品,CoFiToR根据BPR的预测评分来对LS中的候选物品列表再次进行重排,并移除排序靠后的物品,最后得到大小为NP(NP<NS<NE)的候选物品列表LP,即最终的物品推荐列表。 3.2.2算法流程 CoFiToR的算法流程如算法31所示。它包含三个阶段,不同阶段之间通过候选物品列表来实现知识的共享和迁移。需要说明的是,CoFiToR的三个阶段是相对独立的,因此,每个阶段的模型可以根据实际需要进行更改,阶段的数量也可以灵活增减。 算法31CoFiToR算法 1. E阶段 2. 输入: 浏览行为数据 E=(u,i)|(u,i,rui)∈R 3. 输出: 候选物品列表LE 4. 优化式(36)的目标函数 5. 根据式(35)计算(u,i)对的浏览概率 6. 为每个用户生成大小为NE的候选列表LE 7. S阶段 8. 输入: 评分行为数据S=R,E阶段的候选列表LE 9. 输出: 候选物品列表LS 10. 优化式(38)的目标函数 11. 根据式(39)计算(u,i)对的预测评分 12. 为每个用户生成大小为NS的候选列表LS 13. P阶段 14. 输入: 购买行为数据P={(u,i)|(u,i,5)∈R},S阶段的候选列表LS 15. 输出: 候选物品列表LP 16. 优化式(34)的目标函数 17. 根据式(35)计算(u,i)对的购买概率 18. 为每个用户生成大小为NP的候选列表LP 3.2.3代码实现 CoFiToR的算法代码采用Java语言编写,工具是JDK 1.8和代码编辑器Eclipse。它具有三个阶段。第一阶段采用加强版本的BPR算法,其中采样、对评分进行归一化和计算损失函数的代码如下: public static void train() throws IOException { for (int iter = 0; iter < num_iterations; iter++){ for (int iter2 = 0; iter2 < num_train; iter2++){ //随机采样一个(u,i,rui)三元组 int idx = (int) Math.floor(Math.random() * num_train); int u = indexUserTrain[idx]; int i = indexItemTrain[idx]; float rating = indexRatingTrain[idx]; //对评分进行归一化 float r_ui = 0f; if(rtype == 5){ int loc = (int)rating; r_ui = rating_weight[loc]; } else if(rtype == 10){ int loc = (int)(rating*2); r_ui = rating_weight[loc]; } //获取训练集中用户u的评分记录 Map Item_Rating = TrainData.get(u); int j = i; while (true){ //随机采样一个物品j j = (int) Math.floor(Math.random() * m) + 1; //判断物品j是否是负样本 if (ItemSetTrain.contains(j) && !Item_Rating.containsKey(j)) { break; } else { continue; } } //计算损失函数 float r_uij = biasV[i] - biasV[j]; for (int f = 0; f < d; f++){ r_uij += U[u][f] * (V[i][f] - V[j][f]); } float EXP_r_uij = (float) Math.pow(Math.E, r_uij); float loss_uij = -1f / (1f + EXP_r_uij); } } } 第二阶段采用PMF算法,其中采样和计算损失函数的代码如下: public static void train() { for (int iter = 0; iter < num_iterations; iter++){ for (int iter_rand = 0; iter_rand < num_train_target; iter_rand++){ //随机采样一个(u,i,rui)三元组 int rand_case = (int) Math.floor(Math.random() * num_train_target); int userID = indexUserTrain[rand_case]; int itemID = indexItemTrain[rand_case]; float rating = ratingTrain[rand_case]; //计算预测公式和误差 float pred = 0; float err = 0; for (int f = 0; f < d; f++){ pred += U[userID][f] * V[itemID][f]; } pred += g_avg + biasU[userID] + biasV[itemID]; err = rating - pred; } } } 第三阶段采用原始的BPR算法,可以参考第一阶段的代码实现。 3.2.4实验设置 对于实验参数的设置,CoFiToR设置潜在特征向量的维度d=100,学习率γ=0.01,迭代次数T从{100,500,1000}中选取,正则化项上的权衡参数α从{0.1,0.01,0.001}中选取。根据验证集上的NDCG@5指标选择最优的参数值。 3.2.5讨论 CoFiToR算法由三个阶段组成,分别从浏览、评分和购买三种不同但相关的视角看待用户的评分记录。每个阶段都代表用户购物流程中的一个步骤,从粗粒度到细粒度逐步建模用户的偏好,不同阶段之间通过候选物品列表的形式来实现知识的共享与迁移。与ToR模型相比,CoFiToR模型对用户的购买行为建模,因此能够更准确地捕捉用户的偏好信息。需要说明的是,分阶段建模(例如检索、匹配、重排、规则过滤等)在实际应用中也是一种比较常用的做法,因此CoFiToR模型可以看作工业界推荐系统的一个缩影。 CoFiToR是一个基于迁移学习的框架,其基础模型可以替换为其他模型,因此,在未来工作中,可以将其他互补性较强的算法整合到CoFiToR框架中,以进一步提升其推荐性能。此外,CoFiToR使用的模型要么基于单点偏好,要么基于成对偏好,未来可以考虑使用信息检索中常用的基于成列偏好的模型[67]。 3.3上下文感知协同排序 结合多类偏好上下文的矩阵分解(matrix factorization with multiclass preference context,MFMPC)算法[8]将偏好上下文定义为用户已评分的物品,并将其结合到传统的矩阵分解模型中以解决评分预测问题。受到MFMPC算法的启发,上下文感知协同排序(contextaware collaborative ranking,CCR)算法[9]通过在用户偏好的建模过程中融入上下文信息来提高推荐效果。具体而言,它首先从用户的原始评分记录中提取隐含的上下文作为辅助信息,然后将其融入用户偏好的建模过程中,以预测用户对物品的喜好。 3.3.1模型介绍 CCR算法的示意图如图33所示,可以看到它从一个新的视角对评分数据进行建模。具体来说,它将原始的评分矩阵拆分成两个矩阵,一个表示用户的正负反馈,另一个表示用户的偏好上下文; 然后,它通过一个对数几率损失函数来连接这两个矩阵,并从中学习用户和物品的特征; 最后,它根据预测的用户对物品的偏好来对物品进行排序,从而得到最终的物品推荐列表。 图33CCR算法的模型结构示意图(见彩插) CCR算法的建模原理如下。它首先计算每个用户的平均评分值作为其个性化的阈值,即r-u,然后将用户对物品的原始评分值按照以下方式转换成二值评分: yui=+1,rui≥r-u -1,rui<r-u(311) 具体而言,它将yui=+1的记录当作正样本,并将其加入正样本集合SP中,其他样本则当作负样本加入负样本集合SN中。此外,它还随机采样了一部分未观测到的记录加入负样本集合中作为补充,其中未观测样本的采样数是观测样本的ρ倍(ρ为预设的整数)。因此,负样本集合SN由两部分组成,一部分是评分rui<r-u的观测记录,另一部分是采样得到的未观测到的样本(u,i),其中yui=-1。需要注意的是,在训练CCR时,每次迭代都需要从集合SP∪SN中随机采样一个(u,i,yui)三元组。 为了进一步利用用户的评分记录,CCR采用与MFMPC[8]类似的方法,从评分记录中挖掘出有价值的信息,即用户的偏好上下文,以准确地建模用户对物品的偏好。在实际应用中,当用户决定是否购买某个物品时,通常会根据自己之前的购物经历来作决定,即用户对历史物品的偏好会影响其对未交互物品的态度。因此,参照MFMPC[8],CCR在传统的矩阵分解模型中融入用户的偏好上下文: r^ui=(Uu·+MPCu·)VTi·+bu+bi+μ(312) 其中,MPCu·表示用户u的内在偏好上下文,其定义如下: MPCu·=∑r∈M1Jru\{i}∑i′∈Jru\{i}Mri′·(313) 其中,Mri′·是评分值为r的物品i′的潜在特征向量。由式(313)可得,所有的上下文物品根据用户对它们的评分被分为|M|组,这有助于学习不同评分值物品的特征,以及用户对不同评分值物品的偏好。 为了生成每个用户的物品推荐列表,CCR使用对数几率损失函数来最小化真实值和预测值之间的误差,形式化如下: lr^ui,rui=-lnσ(yuir^ui)(314) 如果(u,i,rui)是正样本,yui=1; 否则,yui=-1。 最后,CCR的目标函数形式化如下: argminΘ1n∑nu=1∑mi=1lr^ui,rui+reg(u,i)(315) 其中,reg(u,i)=λ2(Uu·2+Vi·2+b2u+b2i+∑r∈M∑i′∈Jru\{i}Mri′·2)是正则化项,用于防止过拟合; Θ={Uu·,Vi·,bu,bi,μ,Mri′·|u=1,2,…,n;i=1,2,…,m;r∈M}为要学习的模型参数。 3.3.2算法流程 CCR的算法流程如算法32所示。它由参数初始化、评分转换和模型训练三部分组成,并将每个用户和每个物品的潜在特征和偏置项初始化为随机值。 算法32CCR算法 输入: 评分记录 R 输出: TopK物品推荐列表 1. 初始化所有参数 2. for u=1,2,…,n do 3.计算用户u的平均评分r-u 4. end for 5. for eachu,i,rui∈Rdo 6.if rui≥r-u then 7.将(u,i,+1)加入正样本集合SP 8.else 9.将(u,i,-1)加入负样本集合SN 10.end if 11. end for 12. for t=1,2,…,T do 13.有放回地随机采样ρ|R|个未观测的样本,并加入负样本集合SN中 14.for t2=1,2…,SP∪SN do 15.随机采样一个(u,i,yui)三元组 16.根据式(313)计算MPCu· 17.根据式(312)计算r^ui 18.更新模型参数Θ 19.end for 20. end for 3.3.3代码实现 CCR的算法代码采用Java语言编写,工具是JDK 1.8和代码编辑器Eclipse。其中计算用户的内在偏好上下文的代码如下: public static void CCR(float yui, int u, int i) { HashMap>rating_itemSet = Data.TrainData.get(u); //计算Uu_bar_MPC float []Uu_bar_MPC = new float[Data.d]; for (float key : rating_itemSet.keySet()){ HashSetitemSet = rating_itemSet.get(key); int rating_size = itemSet.size(); //计算用户的评分数量,不包含物品i if(itemSet.contains(i)) rating_size = rating_size - 1; if(rating_size == 0) continue; float []temp = new float[Data.d]; //用于存储M中的潜在特征向量的和 if(Data.rtype == 10){ for(int itemID : itemSet){ if(itemID == i) //排除物品i continue; for(int f=0; f itemRatingSet_u=TrainData.get(u); r_ui=itemRatingSet_u.get(i); bar_r_ui=(float)((Math.pow(2,r_ui)-1)/Math.pow(2,5)); //随机采样一个物品j∈J\JEu while(true){ j=(int)Math.floor(Math.random()*m)+1; if(ItemTrainingSet.contains(j)&&!itemRatingSet_u.containsKey(j)) break; } }else{ //随机采样一个(用户,物品)对(u,i)∈P int idx; idx=(int)Math.floor(Math.random()*(num_train-num_train_notFive)); u= indexUserTrainPurchase[idx]; i= indexItemTrainPurchase[idx]; bar_r_ui=1; HashMap itemRatingSet_u=TrainData.get(u); //随机采样一个物品j∈J\JPu while(true){ j=(int)Math.floor(Math.random()*m)+1; if(ItemTrainingSet.contains(j)&&(!itemRatingSet_u.containsKey(j)||itemRatingSet_u.get(j)!=5.0)) break; } } //计算预测公式和损失函数 float r_uij=biasV[i]-biasV[j]; for(int k=0;k