第5章基于异构反馈的评分预测 在解决评分预测问题时,由于缺乏足够的等级评分数据(例如,1~5分的数值评分),协同过滤算法经常面对数据稀疏性的问题。为了缓解这一问题,Koren[1]、Liu[2]和Zhang[3]等除了使用显式反馈等级评分数据之外,还使用了隐式反馈数据(例如,点击、购买、是否评分等对物品没有明确偏好的用户反馈和行为)来帮助提高评分预测的效果。但是,这些研究没有考虑利用其他类型的显式反馈数据来帮助缓解等级评分数据的稀疏性问题,例如,二值评分数据,即0或1的评分,其中,0表示用户对物品不喜欢(而不是指用户对物品的偏好信息缺失),1表示用户对物品喜欢。 本章将重点介绍Pan等[4]和Liang等[5]提出的在迁移学习框架下利用二值评分数据来缓解数据稀疏性问题的两个协同过滤算法。第一个是基于迁移学习的共同矩阵分解算法,又称为迁移共同分解(transfer by collective factorization, TCF)[4],第二个是基于偏好迁移的协同过滤算法,又称为偏好感知迁移(preferenceaware transfer, PAT)[5]。 5.1异构协同过滤(HCF)问题 Pan等[4]和Liang等[5]研究的是面向用户异构显式反馈的评分预测问题,目的是在迁移学习框架中,利用二值评分数据来缓解等级评分数据的稀疏性问题,进而提高预测每个用户u对测试集中未评分物品的等级评分的效果。如图51所示,问题的输入是一组等级评分数据R={u,i,rui|rui∈G},其中G={1,2,…,5},以及一组二值评分数据R~={u,i,r~ui|r~ui∈B},其中B={1,0}。需要说明的是,等级评分数据和二值评分数据是两种不同类型的显式反馈数据,即它们是异构的。本章从迁移学习的视角对用户的两种显式反馈数据进行建模,并把拥有等级评分数据的一方称为目标方,把拥有二值评分数据的一方称为辅助方。表51对本章使用的符号进行了说明。 图51HCF问题示意图 表51HCF问题中的符号及其说明 符号 说明 n 用户数 m 物品数 u∈1,2,…,n 用户ID i,j∈1,2,…,m 物品ID G={1,2,…,5} 用户对物品的等级评分的范围 B={1,0} 用户对物品的二值评分的范围,1表示喜欢,0表示不喜欢 rui∈G 用户u对物品i的等级评分 r~ui∈B 用户u对物品i的二值评分 R={(u,i,rui)} 训练集中的等级评分记录集合 R~={(u,i,r~ui)} 训练集中的二值评分记录集合 J 训练集中所有物品的集合 Ju 训练集中用户u评过分的物品的集合 Jgu,g∈G 训练集中用户u评分为g的物品的集合 yui∈{0,1} 指示变量,1表示用户u对物品i评过分,0表示没有评过分 Pu 训练集中用户u喜欢的物品的集合 Nu 训练集中用户u不喜欢的物品的集合 TE={(u,i,rui)} 测试集中的等级评分记录集合 μ∈R 全局评分平均值 bu∈R 用户u的偏差(或偏置) bi∈R 物品i的偏差(或偏置) d∈R 潜在特征向量的维度 Uu·,Wu·∈R1×d 用户u的潜在特征向量 Vi·,Cpj·,Cnj·,Coi′·,Cgi′·∈R1×d 物品i、j和i′的潜在特征向量 V,Cp,Cn,Co,Cg∈Rm×d 物品的潜在特征矩阵 B,B~∈Rd×d 目标方和辅助方的内部矩阵 r^ui 用户u对物品i的预测评分 rui 用户u对物品i的二值预测评分 γ 学习率 ω 等级评分和二值评分的交互权重参数 α,β 正则化项上的权衡参数 续表 符号 说明 λ 目标方和辅助方目标函数上的权衡参数 δg,δo,δp,δn∈{0,1} 指示变量,是否引入相应的特征向量 wp,wn 正反馈和负反馈的权重 T 算法的迭代次数 5.2迁移共同分解 在基于迁移学习的协同过滤算法中,一种典型的解决数据稀疏性问题的思路是将辅助方的二值评分数据(喜欢或不喜欢)中隐含的知识迁移到目标方,以提高目标方的等级评分预测效果。Pan等[4]提出的TCF在迁移学习框架中联合建模了目标方的等级评分数据和辅助方的包含喜欢和不喜欢的二值评分数据,有效地缓解了数据的稀疏性问题。本节将详细介绍TCF的技术细节。 5.2.1技术细节 TCF [4]的目标方和辅助方的预测公式分别如下所示: r^ui=Uu·BVTi· (51) rui=Uu·B~VTi·(52) 其中,目标方和辅助方共享用户的潜在特征向量为Uu·和物品的潜在特征向量为Vi·; B和B~分别用于捕捉目标方数据和辅助方数据中特有的信息(即目标方和辅助方的矩阵U中的行和矩阵VT中的列之间的相关性),因为目标方的等级评分数据和辅助方的二值评分数据的语义和分布等信息很可能是不一样的。 TCF[4]的目标函数如下所示: minΘ∑nu=1∑mi=1yui12rui-r^ui2+α2(Uu·2+Vi·2)+ λ∑nu=1∑mi=1y~ui12r~ui-rui2+α2(Uu·2+Vi·2)+β2B2F+ λβ2B~2F,s.t.U,V∈D(53) 其中,Θ∈{U,V,B,B~}表示模型参数; yui和y~ui分别是目标方和辅助方的指示变量,它们为1时表示用户u对物品i有评分,为0时表示用户u对物品i没有评分; D表示潜在变量的值域。为了发现潜在的主题,并降低噪声的影响,D可以表示成DR={U∈Rn×d,V∈Rm×d}或D⊥=DR∩{UTU=I,VTV=I}。因此,TCF有两个变体: 当D表示成DR时,TCF为共同矩阵三分解(collective matrix trifactorization, CMTF)[4],当D表示成D⊥时,TCF为共同奇异值分解(collective SVD, CSVD)[4]。 令fu=∑mi=1yui12rui-r^ui2+α2(Uu·2+Vi·2)+β2B2+λ∑mi=1y~ui12r~ui-rui2+α2(Uu·2+Vi·2)+λβ2B~2,给定B和V,可以得到CMTF中用户的潜在特征矩阵U的解析解,推导过程如下所示: fuUu·=∑mi=1yui-rui+r^uiVi·BT+αUu·+λ∑mi=1y~ui-r~ui+ruiVi·B~T+αUu· =-∑mi=1(yuiruiVi·BT+λy~uir~uiVi·B~T)+αUu·∑mi=1(yui+λy~ui)+ Uu·∑mi=1(yuiBVTi·Vi·BT+λy~uiB~VTi·Vi·B~T)(54) 令fuUu·=0,推导出用户u的潜在特征向量Uu·的计算公式如下所示: Uu·=buC-1u(55) 其中,Cu=∑mi=1(yuiBVTi·Vi·BT+λy~uiB~VTi·Vi·B~T)+α∑mi=1(yui+λy~ui)I; bu=∑mi=1(yuiruiVi·BT+λy~uir~uiVi·B~T)。 类似地,给定B和U,可以得到CMTF中物品的潜在特征矩阵V的解析解,物品i的潜在特征向量Vi·的计算公式如下所示: Vi·=biC-1i(56) 其中,Ci=∑nu=1(yuiBTUTu·Uu·B+λy~uiB~TUTu·Uu·B~)+α∑nu=1(yui+λy~ui)I; bi=∑nu=1(yuiruiUu·B+λy~uir~uiUu·B~)。需要说明的是,式(55)和式(56)中解析解的更新规则可以视为最小二乘(alternating least squares, ALS)方法[6]的推广。 在CSVD中,值域D⊥的限制起到了与正则化项类似的效果,将式(53)中的正则化项去掉,可以得到新的目标函数: minU,V12‖Y⊙R-UBVT‖2F+λ2‖Y~⊙R~-UB~VT‖2F, s.t.UTU=I,VTV=I(57) 其中,⊙表示矩阵中的元素逐项相乘。 令f=12‖Y⊙R-UBVT‖2F+λ2‖Y~⊙R~-UB~VT‖2F,U的梯度计算公式如下所示: fU=(Y⊙UBVT-R)VBT+λ(Y~⊙(UB~VT-R~))VB~T(58) 通过格拉斯曼流形(Grassmann manifold)梯度下降算法[7]可以得到U的更新公式如下所示: U←U-γI-UUTfU=U-γU(59) 将式(59)代入目标函数(见式(57))中,可以得到如下公式: g(γ)=12‖Y⊙R-U-γUBVT‖2F+λ2‖Y~⊙R~-U-γUB~VT‖2F =12‖Y⊙R-UBVT+γY⊙(UBVT)‖2F+λ2‖Y~⊙R~-UB~VT+γY~⊙(UB~VT)‖2F(510) 令t1=Y⊙(R-UBVT),t~1=Y~⊙(R~-UB~VT),t2=Y⊙(UBVT),t~2=Y~⊙(UB~VT),有g(γ)=12‖t1+γt2‖2F+λ2‖t~1+γt~2‖2F,以及梯度如下: g(γ)γ=trtT1t2+γtrtT2t2+λ[trt~T1t~2+γtrt~T2t~2](511) 其中,tr·是矩阵的迹(trace)。令g(γ)γ=0,可以得到γ=-trtT1t2-λtrt~T1t~2trtT2t2+λtrt~T2t~2。 类似地,可以得到物品的潜在特征矩阵V的更新规则如下所示: V←V-γV(512) 其中,V=I-VVTfV,fV=(Y⊙UBVT-R)UB+λ(Y~⊙(UB~VT-R~))UB~。 给定U和V,目标方和辅助方可以分别计算B和B~。令FR~UBVT=∑nu=1∑mi=1yui12rui-r^ui2+α2(Uu·2+Vi·2)+β2B2F,有: FR~UBVT∝∑nu=1∑mi=1yui12rui-r^ui2+β2B2F =12‖Y⊙R-UBVT‖2F+β2B2F(513) 因此,可以得到如下等价的目标函数: minB12‖Y⊙R-UBVT‖2F+β2B2F(514) 其中,B的计算可以与最小二乘SVM问题[8]中的w的计算完全相同; w=vec(B)=[BT·1…BT·d]T∈Rd2×1是由矩阵B的列拼接而成的向量。当yui=1时,可以构造样本{(xui,rui)},其中xui=vec(UTu·Vi·)∈Rd2×1。因此,我们可以得到与最小二乘SVM问题等价的目标函数,如下所示: minw12‖r-Xw‖2F+β2w2F(515) 其中,数据矩阵X=[…xui…]T∈Rp×d2(当yui=1时),评分r∈{1,2,3,4,5}p×1,p=∑nu=1∑mi=1yui。令w=-XTr-Xw+βw=0,我们可以得到: w=(XTX+βI)-1XTr(516) 需要说明的是,B和w可以被视为线性紧算子(linear compact operator),它们能够使用各种现有的离线工具来有效求解。 5.2.2算法流程 构建TCF[4]模型的流程如下: ①对目标方的等级评分数据的范围进行处理,使得它与辅助方的二值评分数据的范围一致; ②在算法的第一次迭代中,对U和V进行随机初始化(对于CMTF)或使用辅助方的二值评分数据的训练集R~的SVD结果[9](对于CSVD),然后通过式(516)计算B和B~; ③在算法的第t(t>1)次迭代中,更新U,V,B,B~。重复步骤②和步骤③,直到收敛。在算法51中,我们可以看到TCF算法流程的伪代码。 算法51TCF算法 输入: 目标方的等级评分数据的训练集R,辅助方的二值评分数据的训练集R~ 输出: 每个用户u对测试集JE中的物品i的预测评分 1. 通过rui=rui-14,rui∈R对目标方的等级评分进行处理 2. if t==1 then 3. 对于CMTF,随机初始化U和V; 对于CSVD,使用R~的SVD结果 4. 通过式(516)计算B和B~ 5. end if 6. for t=2,3,…,T do 7. 对于CMTF,T1=1; 对于CSVD,T1=20 8.for t1=1,2,…,T1 do 9. 固定V和B,对于CMTF,通过式(55)更新U; 对于 CSVD,通过式(59)更新U 10. 固定U和B,对于CMTF,通过式(56)更新V; 对于 CSVD,通过式(512)更新V 11. if模型已收敛 then 12. 跳出当前循环 13. end if 14.end for 15.固定U和V,通过式(516)更新B和B~ 16.if模型已收敛 then 17. 跳出当前循环 18. end if 19. end for 5.2.3代码实现 TCF代码的运行环境为MATLAB,算法51的第7~15行对应的核心代码如下: //CMTF //固定U和B,通过式(5-6)更新V U2 = [U*B; U*B_aux]; V = MF_U(vec_V(:,[2 1 3]), tradeoff_alpha_V, V, U2, tradeoff_lambda, num_user); //固定V和B,通过式(5-5)更新U V2 = [V*B'; V*B_aux']; U = MF_U(vec_U, tradeoff_alpha_U, U, V2, tradeoff_lambda, num_item); //固定U和V,通过式(5-16)更新B和B~ B = EstimateB(U,V,train_vec,tradeoff_beta); B_aux = EstimateB(U,V,aux_vec,tradeoff_beta_aux); //CSVD for iterUV = 1:20 //固定V和B,通过式(5-9)更新U GradU = CalGradU(U,V,B,B_aux,train_vec,aux_vec,tradeoff_lambda); gamma = StepSize_gamma( U, GradU, V, B, B_aux, train_vec, aux_vec, tradeoff_lambda ); U = U - gamma*GradU; //固定U和B,通过式(5-12)更新V GradV = CalGradU(V,U,B',B_aux',train_vec(:,[2 1 3]),aux_vec(:,[2 1 3]),tradeoff_lambda); gamma = StepSize_gamma( V, GradV, U, B', B_aux', train_vec(:,[2 1 3]), aux_vec(:,[2 1 3]), tradeoff_lambda ); V = V - gamma*GradV; end //固定U和V,通过式(5-16)更新B和B和B~ B = EstimateB(U, V, train_vec, tradeoff_beta); B_aux = EstimateB(U, V, aux_vec, tradeoff_beta_aux); 5.2.4实验设置 对于CMTF和CSVD,Pan等[4]设置模型的潜在特征向量的维度d=10,算法的总迭代次数T=100,辅助方的二值评分数据的权重λ从0.01,0.1,1中进行选择,对所有的数据集都有λ=0.1。对于CMTF,正则化项上的权衡参数β=1,正则化项上的权衡参数α从0.01,0.1,1中进行选择,对所有的数据集都有α=0.1。 5.2.5讨论 Pan等[4]针对协同过滤中的数据稀疏性问题,提出了一个基于迁移学习的共同矩阵分解(TCF)算法。TCF在目标方和辅助方的用户和物品都对齐的情况下,将辅助方的二值评分数据(喜欢或不喜欢)中的知识迁移到目标方,以提高目标方的等级评分预测效果。在MoviePilot[10]和Netflix这两个数据集上的实验结果表明,在不同的数据稀疏程度下,CMTF和CSVD的效果都优于基准方法。 未来还可以在其他的一些问题设置中进一步拓展TCF框架,例如,当目标方和辅助方的用户和物品只有部分对齐时、当需要考虑目标方和辅助方之间的用户隐私和数据安全时[11]等。 5.3偏好感知迁移 迁移混合分解模型(transfer by mixed factorization,TMF)[12]利用辅助方的二值评分数据中用户的隐式偏好上下文来帮助提高目标方的评分预测效果,而SVD++[13]和MFMPC[14]利用了目标方的等级评分数据中用户的隐式偏好上下文来更好地学习用户的个性化偏好。为了结合两者的优点,Liang等[5]基于迁移学习框架提出共享目标方和辅助方的评分信息,并以互补的方式将等级评分、二值评分和目标方与辅助方的隐式偏好上下文这三者所包含的信息融合在一起,进而设计了一个偏好感知迁移学习(PAT)算法,以提高用户对物品的评分预测效果。 5.3.1技术细节 共同矩阵分解(collective matrix factorization,CMF)算法[15]利用目标方和辅助方的两种不同类型的显式反馈数据来联合构建推荐模型。在模型的构建过程中,目标方和辅助方共享物品的潜在特征向量Vi·。目标方和辅助方的评分预测公式分别如下所示: r^ui=μ+bu+bi+Uu·VTi·(517) rui=Wu·VTi·(518) 我们可以发现,CMF在以等级评分预测为目标来构建模型时,并没有考虑隐式偏好上下文的信息。 根据SVD++[13]、MFMPC[14]和TMF[12]的建模方式,Liang等[5]得到如下偏好上下文: ou·=δo1Ju\{i}∑i′∈Ju\{i}Coi′·(519) gu·=δg∑g∈G1Jgu\{i}∑i′∈Jgu\{i}Cgi′·(520) pu·=δpwp1Pu∑j∈PuCpj·(521) nu·=δnwn1Nu∑j∈NuCnj·(522) 其中,ou·表示单类偏好上下文(oneclass preference context, OPC),其认为用户u对物品i的预测评分除了与物品i有关以外,还与用户u评过分的其他物品i′∈Ju\{i}有关; gu·表示多类偏好上下文(multiclass preference context, MPC),其在单类偏好上下文的基础上将用户u对除物品i以外的其他物品i′∈Ju\{i}根据评分等级g∈G进行了分类; pu·表示正反馈偏好上下文,其认为对物品集合具有相似喜好的用户在对其他物品的喜好上也是相似的; nu·表示负反馈偏好上下文,其认为对物品集合具有相似不喜好的用户在对其他物品的不喜好上也是相似的; δo,δg,δp,δn∈{0,1}是指示变量,为1时表示引入对应的偏好上下文,为0时表示不引入对应的偏好上下文; wp和wn分别表示正反馈和负反馈上的权重。 Liang等[5]将这四类偏好上下文融入迁移学习框架中,分别得到以下目标方和辅助方的预测公式: r^ui=μ+bu+bi+(Uu·+ou·+gu·+pu·+nu·)VTi·(523) rui=Wu·VTi·(524) 其中,ou·是SVD++[13]中的偏好上下文,它会使得具有相似单类偏好上下文的用户的潜在特征向量接近。需要说明的是,Liang等将偏好上下文融入目标方的预测公式中而不是辅助方的预测公式中,这是因为Liang等的研究目标是等级评分预测,而不是二值评分预测。在图52中,可以看到目标方和辅助方的整体预测规则,图中的绿色虚线表示引入目标方和辅助方之间的交互。需要说明的是,图52中的Cgj·表示MFMPC[14]中的多类偏好上下文,如果该项不考虑评分的等级,就会退化为SVD++[13]中的单类偏好上下文Coj·,对应地,PAT也退化为PATOPC[5]。 图52PAT算法的模型结构示意图(见彩插) PAT[5]的目标函数与TMF[12]的类似,如下所示: minΘ∑nu=1∑mi=1yuifui+λ∑nu=1∑mi=1y~uif~ui(525) 其中,yui和y~ui分别是目标方和辅助方的指示变量,为1时表示用户u对物品i有评分,为0时表示用户u对物品i没有评分,fui=12rui-r^ui2+α2(Uu·2+Vi·2+b2u+b2i+δp∑j∈Pu‖Cpj·‖2+δn∑j∈Nu‖Cnj·‖2+δo∑i′∈Ju\{i}‖Coi′·‖2+δg∑g∈G∑i′∈Jgu\{i}‖Cgi′·‖2)是目标方定义在用户u和物品i上的目标函数,f~ui=12r~ui-rui2+α2(Wu·2+Vi·2)是辅助方定义在用户u和物品i上的目标函数,Θ={μ,bu,bi,Uu·,Vi·,Cpj·,Cnj·,Coi′·,Cgi′·|u=1,2,…,n; i,i′,j=1,2,…,m}表示模型的参数。 关于fui,模型参数的梯度如下所示: μ=fuiμ=-eui(526) bu=fuibu=-eui+αbu(527) bi=fuibi=-eui+αbi(528) Uu·=fuiUu·=-euiVi·+αUu·(529) Vi·=fuiVi·=-eui(ωUu·+1-ωWu·+pu·+nu·+ou·+gu·)+αVi·(530) Coi′·=fuiCoi′·=δo(-eui1Ju\{i}Vi·+αCoi′·),i′∈Ju\{i}(531) Cgi′·=fuiCgi′·=δg(-eui1Jgu\{i}Vi·+αCoi′·),i′∈Jgu\{i},g∈G(532) 其中,eui=rui-r^ui是目标方中真实评分与预测评分之间的差,ωUu·+1-ωWu·用于引入目标方和辅助方的用户潜在特征向量之间的交互[16]。 关于f~ui,模型参数的梯度如下所示: Cpj·=fuiCpj·=δp(-euiwp1PuVi·+αCpj·),j∈Pu(533) Cnj·=fuiCnj·=δn(-euiwn1NuVi·+αCnj·),j∈Nu(534) Wu·=f~uiWu·=λ(-e~uiVi·+αWu·)(535) Vi·=f~uiVi·=λ(-e~ui(ωWu·+1-ωUu·)+αVi·)(536) 其中,e~ui=r~ui-rui是辅助方中真实评分与预测评分之间的差。 目标方和辅助方的模型参数的更新规则如下所示: θ=θ-γθ(537) 其中,γ为学习率,γ>0。 5.3.2算法流程 构建PAT[5]模型的流程如下: ①先从目标方数据和辅助方数据的并集R∪R~中随机挑选一条评分记录(u,i,rui,r~ui),如果评分记录来源于目标方,那么该评分记录用于计算目标方中的模型参数(μ,bu,bi,Uu·,Vi·,Cpj·,Cnj·,Coi′·和Cgi′·)的梯度,如果评分记录来源于辅助方,那么该评分记录用于计算辅助方中的模型参数(Vi·和Wu·)的梯度; ②利用步骤①计算得到的模型参数的梯度更新对应的模型参数; ③更新学习率。重复上述三个步骤,直到收敛。在算法52中,我们可以看到PAT算法流程的伪代码。 算法52PAT算法 输入: 目标方的等级评分数据训练集R,辅助方的二值评分数据训练集R~ 输出: 每个用户u对测试集TE中物品i的预测评分 1. for t=1,2,…,T do 2. for t1=1,2,…,|R∪R~| do 3. 从目标方和辅助方的数据中随机挑选一条评分记录 4. 根据评分记录的来源方,计算对应方中的模型参数梯度 5. 利用模型参数梯度通过式(537)更新模型参数 6. end for 7. 通过γ=γ×0.9更新学习率 8. end for 5.3.3代码实现 PAT代码的运行环境为JDK 1.8和MyEclipse 2020,算法52的第3~5行对应的核心代码如下: //从目标方和辅助方的数据并集中随机挑选一条评分记录 int rand_case = (int) Math.floor( Math.random() * Data.num_train ); int userID = Data.indexUserTrain[rand_case]; int itemID = Data.indexItemTrain[rand_case]; float rating = Data.ratingTrain[rand_case]; //如果随机挑选的评分记录属于辅助方,那么利用该评分计算辅助方的用户和物品梯度(Wu·和 //Vi·),并更新对应的用户和物品的潜在特征向量(Wu·和Vi·) if( rand_case>Data.num_train_target && Data.lambda>0 ) { float auxiliary_pred = 0; float auxiliary_err = 0; for (int f=0; f0 ) { HashSet itemSet = Data.Train_ExplicitFeedbacksGraded.get(userID).get(g); //计算等级评分个数并开根号 float explicit_feedback_num_u_sqrt = 0; if(itemSet.contains(itemID) ) { if( itemSet.size()>1 ) { explicit_feedback_num_u_sqrt = (float) Math.sqrt( Data.user_graded_rating_number[userID][g] - 1 ); } } else { explicit_feedback_num_u_sqrt = (float) Math.sqrt( Data.user_graded_rating_number[userID][g] ); } if (explicit_feedback_num_u_sqrt>0) { //计算多类偏好上下文Cgi′·对应的模型参数梯度Cgi′· for( int i2 : itemSet ) { if(i2 != itemID) { for(int f=0; f0 ) //如果引入了 //"喜欢"偏好上下文Cpj· { HashSet itemSet = Data.AuxiliaryDataPositive.get(userID); for( int auxiliary_itemID : itemSet ) { for (int f=0; f0 ) { HashSet itemSet = Data.AuxiliaryDataPositive.get(userID); for(int auxiliary_itemID : itemSet) { for (int f=0; f0 ) { HashSet itemSet = Data.Train_ExplicitFeedbacksGraded.get(userID).get(g); float explicit_feedback_num_u_sqrt = 0; if(itemSet.contains(itemID) ) { if( itemSet.size()>1 ) { explicit_feedback_num_u_sqrt = (float) Math.sqrt( Data.user_graded_rating_number[userID][g] - 1 ); } } else { explicit_feedback_num_u_sqrt = (float) Math.sqrt( Data.user_graded_rating_number[userID][g] ); } if(explicit_feedback_num_u_sqrt>0) { for( int i2 : itemSet ) { if(i2 != itemID) { for (int f=0; f