本文参考文献
刘建平:https://www.cnblogs.com/pinard/p/9128682.html
BPR 推荐模型基于贝叶斯理论在先验知识下极大化后验概率,实现从一个用户(U)-项目( I )矩阵训练出多个矩阵,且一个矩阵表示一个用户的项目偏好情况来获得用户多个项目的偏序关系下来进行排名的推荐系统。
1.构造BPR模型
此处主要讲的是对于用户U,相对于 j 项目他更喜欢 i .然后假设了每个用户之间的偏好行为相互独立和同一用户对不同物品的偏序相互独立,此处的三种关系我觉得不用深究。
然后其中的矩阵分解模型就是随机生成两个矩阵,然后进行梯度下降更新两个矩阵。目的是让他能拟合原始的评分矩阵。注意区分原始矩阵和最后得到的矩阵。最后得到的矩阵可以用来预测用户没有评分项目的得分。
2.BPR的算法优化思路
BPR 基于最大后验估计P(W,H|>u)来求解模型参数W,H这里我们用θ来表示参数W和H, >u代表用户u对应的所有商品的全序关系,则优化目标是P(θ|>u)。根据贝叶斯公式,我们有:
可以简化为这个形式
3. BPR算法流程
input:训练集D三元组,梯度步长αα, 正则化参数λλ,分解矩阵维度k。
output:模型参数,矩阵W,H
1. 随机初始化矩阵W,H。(随机矩阵)
2. 迭代更新模型参数:(关键步骤)
3. 如果W,H收敛,则算法结束,输出W,H,否则回到步骤2.
4.最终得到两个矩阵(相乘得到结果),就可以完成预测。
4.tensorflow实现
数据集位置:http://files.grouplens.org/datasets/movielens选择100k的。
代码:
import numpy as np
import tensorflow as tf
import os
import random
from collections import defaultdict
'''
196 242 3 881250949
186 302 3 891717742
22 377 1 878887116
244 51 2 880606923
166 346 1 886397596
'''
def load_data():
user_ratings = defaultdict(set)
max_u_id = -1
max_i_id = -1
with open('data/u.data','r') as f:
for line in f.readlines():
u,i,_,_ = line.split("\t")
u = int(u)
i = int(i)
user_ratings[u].add(i)
max_u_id = max(u,max_u_id)
max_i_id = max(i,max_i_id)
print("max_u_id:",max_u_id)
print("max_i_idL",max_i_id)
return max_u_id,max_i_id,user_ratings
def generate_test(user_ratings):
"""
对每一个用户u,在user_ratings中随机找到他评分过的一部电影i,保存在user_ratings_test,
后面构造训练集和测试集需要用到。
"""
user_test = dict()
for u,i_list in user_ratings.items():
user_test[u] = random.sample(user_ratings[u],1)[0]
return user_test
#user_count,item_count,user_ratings = load_data()
#user_ratings_test = generate_test(user_ratings)
def generate_train_batch(user_ratings,user_ratings_test,item_count,batch_size=512):#训练集样本
"""
构造训练用的三元组从user_ratings随机抽出,而j也是从总的电影集中随机抽出,当然j必须保证(u,j)不在user_ratings中
"""
t = []
对于随机抽出的用户u,i可以
for b in range(batch_size):
u = random.sample(user_ratings.keys(),1)[0]
i = random.sample(user_ratings[u],1)[0]
while i==user_ratings_test[u]:
i = random.sample(user_ratings[u],1)[0] #不在user_test[]里
j = random.randint(1,item_count)
while j in user_ratings[u]:
j = random.randint(1,item_count) #j不在user_ratings[]里 ,没有评过分的
t.append([u,i,j])
return np.asarray(t)
def generate_test_batch(user_ratings,user_ratings_test,item_count):#测试集样本
"""
对于每个用户u,它的评分电影i是我们在user_ratings_test中随机抽取的,它的j是用户u所有没有评分过的电影集合,
比如用户u有1000部电影没有评分,那么这里该用户的测试集样本就有1000个
"""
for u in user_ratings.keys():
t = []
i = user_ratings_test[u]
for j in range(1,item_count + 1): #item_count = 1682
if not(j in user_ratings[u]):
t.append([u,i,j])
yield np.asarray(t)
def bpr_mf(user_count,item_count,hidden_dim): #user_count = 943 hidden_dim下面取20
u = tf.placeholder(tf.int32,[None])
i = tf.placeholder(tf.int32,[None])
j = tf.placeholder(tf.int32,[None])
#user_emb_w对应矩阵W item_emb_w对应矩阵H hidden_dim就是我们矩阵分解的隐含维度k。
user_emb_w = tf.get_variable("user_emb_w", [user_count + 1, hidden_dim],
initializer=tf.random_normal_initializer(0, 0.1))
item_emb_w = tf.get_variable("item_emb_w", [item_count + 1, hidden_dim],
initializer=tf.random_normal_initializer(0, 0.1))
u_emb = tf.nn.embedding_lookup(user_emb_w, u)
i_emb = tf.nn.embedding_lookup(item_emb_w, i)
j_emb = tf.nn.embedding_lookup(item_emb_w, j)
x = tf.reduce_sum(tf.multiply(u_emb,(i_emb-j_emb)),1,keep_dims=True)
mf_auc = tf.reduce_mean(tf.to_float(x>0))
l2_norm = tf.add_n([
tf.reduce_sum(tf.multiply(u_emb, u_emb)),
tf.reduce_sum(tf.multiply(i_emb, i_emb)),
tf.reduce_sum(tf.multiply(j_emb, j_emb))
])
regulation_rate = 0.0001 #正则化参数
bprloss = regulation_rate * l2_norm - tf.reduce_mean(tf.log(tf.sigmoid(x)))
train_op = tf.train.GradientDescentOptimizer(0.01).minimize(bprloss)
return u, i, j, mf_auc, bprloss, train_op
user_count,item_count,user_ratings = load_data()
user_ratings_test = generate_test(user_ratings)
with tf.Session() as sess:
u,i,j,mf_auc,bprloss,train_op = bpr_mf(user_count,item_count,20)
sess.run(tf.global_variables_initializer())
for epoch in range(1,4):
_batch_bprloss = 0
for k in range(1,5000): #uniform samples from training set
uij = generate_train_batch(user_ratings,user_ratings_test,item_count) #三元组
_bprloss,_train_op = sess.run([bprloss,train_op],
feed_dict={u:uij[:,0], i:uij[:,1], j:uij[:,2]})
_batch_bprloss += _bprloss
print("epoch:",epoch)
print("bpr_loss:",_batch_bprloss / k)
print("_train_op")
user_count = 0
_auc_sum = 0.0
for t_uij in generate_test_batch(user_ratings, user_ratings_test, item_count):
_auc, _test_bprloss = sess.run([mf_auc, bprloss],
feed_dict={u:t_uij[:, 0], i:t_uij[:, 1], j:t_uij[:, 2]}
)
user_count += 1
_auc_sum += _auc
print("test_loss: ", _test_bprloss, "test_auc: ", _auc_sum / user_count)
print("")
variable_names = [v.name for v in tf.trainable_variables()]
values = sess.run(variable_names)
for k, v in zip(variable_names, values):
print("Variable: ", k)
print("Shape: ", v.shape)
print(v)
# 0号用户对这个用户对所有电影的预测评分
#现在我们已经得到了W,H矩阵,就可以对任意一个用户u的评分排序了。注意输出的W,H矩阵分别在values[0]和values[1]中。
#那么我们如何才能对某个用户推荐呢?这里我们以第一个用户为例,它在W中对应的w0向量为value[0][0],那么我们很容
# 易求出这个用户对所有电影的预测评分
session1 = tf.Session()
u1_dim = tf.expand_dims(values[0][0], 0)
u1_all = tf.matmul(u1_dim, values[1],transpose_b=True)
result_1 = session1.run(u1_all)
print (result_1)
print("以下是给用户0的推荐:")
p = np.squeeze(result_1) #去除多余的维度
p[np.argsort(p)[:-5]] = 0 #p中按其数值大小 依次输出最小的索引值
for index in range(len(p)):
if p[index] != 0:
print (index, p[index])
output:
以下是给用户0的推荐:
54 0.1907271
77 0.17746378
828 0.17181025
1043 0.16989286
1113 0.17458326