ML_BPR小结

本文参考文献

刘建平:https://www.cnblogs.com/pinard/p/9128682.html

论文来源:https://yun.baidu.com/share/link?shareid=2431171172&uk=1009675359&errno=0&errmsg=Auth%20Login%20Sucess&&bduss=&ssnerror=0&traceid=

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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值