迁移排序(transfer to rank, ToR)是一种简单有效的算法,包含浏览和评分两个阶段。它首先使用用户的浏览行为数据进行全局偏好学习,然后利用评分行为数据来进一步优化候选物品列表。ToR模型虽然在一定程度上模拟了用户的购物过程,但忽略了用户在购买选择上的差异,也就是说,某些用户虽然给物品打了高分,但他们不一定会购买该物品。为了解决此问题,粗精迁移排序(coarse-to-fine transfer to rank, CoFiToR)将用户购物过程进一步细分为三个阶段,即浏览阶段(E阶段)、评分阶段(S阶段)和购买阶段(P阶段),对应于三个具体问题:
(1)用户是否会浏览一个物品?
(2)用户浏览一个物品后,会给它评多少分?
(3)用户最终是否会购买该物品?
01、算法流程
CoFiToR的算法流程如算法1所示。它包含三个阶段,不同阶段之间通过候选物品列表来实现知识的共享和迁移。需要说明的是,CoFiToR的三个阶段是相对独立的,因此,每个阶段的模型可以根据实际需要进行更改,阶段的数量也可以灵活增减。
算法1 CoFiToR算法
02、代码实现
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,r_ui)三元组
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<Integer, Float> 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,r_ui)三元组
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算法,可以参考第一阶段的代码实现。