用tensorflow学习贝叶斯个性化排序(BPR)

    在贝叶斯个性化排序(BPR)算法小结中,我们对贝叶斯个性化排序(Bayesian Personalized Ranking, 以下简称BPR)的原理做了讨论,本文我们将从实践的角度来使用BPR做一个简单的推荐。由于现有主流开源类库都没有BPR,同时它又比较简单,因此用tensorflow自己实现一个简单的BPR的算法,下面我们开始吧。

1. BPR算法回顾

    BPR算法是基于矩阵分解的排序算法,它的算法训练集是一个个的三元组$<u,i,j>$,表示对用户u来说,商品i的优先级要高于商品j。训练成果是两个分解后的矩阵$W$和$H$,假设有m个用户,n个物品,那么$W$的维度是$m \times k$, $H$的维度是$n \times k$。其中k是一个需要自己定义的较小的维度。对于任意一个用户u,我们可以计算出它对商品i的排序评分为$\overline{x}_{ui} = w_u \bullet h_i$。将u对所有物品的排序评分中找出最大的若干个,就是我们对用户u的真正的推荐集合。

2. 基于movieLens 100K做BPR推荐

    本文我们基于MovieLens 100K的数据做BPR推荐示例,数据下载链接在这。这个数据集有943个用户对1682部电影的打分。由于BPR是排序算法,因此数据集里的打分会被我们忽略,主要是假设用户看过的电影会比用户满意看的电影的排序评分高。由于tensorflow需要批量梯度下降,因此我们需要自己划分若干批训练集和测试集。

3. 算法流程

    下面我们开始算法的流程,参考了github上一个较旧的BPR代码于此,有删改和增强。

    首先是载入类库和数据,代码如下:

import numpy
import tensorflow as tf
import os
import random
from collections import defaultdict

def load_data(data_path):
    user_ratings = defaultdict(set)
    max_u_id = -1
    max_i_id = -1
    with open(data_path, ‘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_id:", max_i_id)
    return max_u_id, max_i_id, user_ratings

data_path = os.path.join(‘D:\\tmp\\ml-100k‘, ‘u.data‘)
user_count, item_count, user_ratings = load_data(data_path)

    输出为数据集里面的用户数和电影数。同时,每个用户看过的电影都保存在user_ratings中。

max_u_id: 943
max_i_id: 1682

    下面我们会对每一个用户u,在user_ratings中随机找到他评分过的一部电影i,保存在user_ratings_test,后面构造训练集和测试集需要用到。

def generate_test(user_ratings):
    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_ratings_test = generate_test(user_ratings)

    接着我们需要得到TensorFlow迭代用的若干批训练集,获取训练集的代码如下,主要是根据user_ratings找到若干训练用的三元组$<u,i,j>$,对于随机抽出的用户u,i可以从user_ratings随机抽出,而j也是从总的电影集中随机抽出,当然j必须保证$(u,j)$不出现在user_ratings中。

def generate_train_batch(user_ratings, user_ratings_test, item_count, batch_size=512):
    t = []
    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]

        j = random.randint(1, item_count)
        while j in user_ratings[u]:
            j = random.randint(1, item_count)
        t.append([u, i, j])
    return numpy.asarray(t)

    下一步是产生测试集三元组$<u,i,j>$。对于每个用户u,它的评分电影i是我们在user_ratings_test中随机抽取的,它的j是用户u所有没有评分过的电影集合,比如用户u有1000部电影没有评分,那么这里该用户的测试集样本就有1000个。

def generate_test_batch(user_ratings, user_ratings_test, item_count):
    for u in user_ratings.keys():
        t = []
        i = user_ratings_test[u]
        for j in range(1, item_count+1):
            if not (j in user_ratings[u]):
                t.append([u, i, j])
        yield numpy.asarray(t)

    有了训练集和测试集,下面是用TensorFlow构建BPR算法的数据流,代码如下,其中hidden_dim就是我们矩阵分解的隐含维度k。user_emb_w对应矩阵$W$, item_emb_w对应矩阵$H$。如果大家看过之前写的BPR的算法原理篇,下面的损失函数的构造,相信大家都会很熟悉。

def bpr_mf(user_count, item_count, hidden_dim):
    u = tf.placeholder(tf.int32, [None])
    i = tf.placeholder(tf.int32, [None])
    j = tf.placeholder(tf.int32, [None])

    with tf.device("/cpu:0"):
        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)

    # MF predict: u_i > u_j
    x = tf.reduce_sum(tf.multiply(u_emb, (i_emb - j_emb)), 1, keep_dims=True)

    # AUC for one user:
    # reasonable iff all (u,i,j) pairs are from the same user
    #
    # average AUC = mean( auc for each user in test set)
    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

    有了算法的数据流图,训练集和测试集也有了,现在我们可以训练模型求解$W,H$这两个矩阵了,注意我们在原理篇是最大化对数后验估计函数, 而这里是最小化取了负号后对应的对数后验估计函数,实际是一样的。代码如下:

with tf.Graph().as_default(), tf.Session() as session:
    u, i, j, mf_auc, bprloss, train_op = bpr_mf(user_count, item_count, 20)
    session.run(tf.initialize_all_variables())
    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 = session.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

        # each batch will return only one user‘s auc
        for t_uij in generate_test_batch(user_ratings, user_ratings_test, item_count):

            _auc, _test_bprloss = session.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 = session.run(variable_names)
    for k,v in zip(variable_names, values):
        print("Variable: ", k)
        print("Shape: ", v.shape)
        print(v)

    这里我k取了20, 迭代次数3, 主要是为了快速输出结果。如果要做一个较好的BPR算法,需要对k值进行选择迭代,并且迭代次数也要更多一些。这里我的输出如下,供参考。

epoch:  1
bpr_loss:  0.7236263042427249
_train_op
test_loss:  0.76150036 test_auc:  0.4852939894020929

epoch:  2
bpr_loss:  0.7229681559433149
_train_op
test_loss:  0.76061743 test_auc:  0.48528061393838007

epoch:  3
bpr_loss:  0.7223725006756341
_train_op
test_loss:  0.7597519 test_auc:  0.4852617720521252

Variable:  user_emb_w:0
Shape:  (944, 20)
[[ 0.08105529  0.04270628 -0.12196594 ...  0.02729403  0.1556453
  -0.07148876]
 [ 0.0729574   0.01720054 -0.08198593 ...  0.05565814 -0.0372898
   0.11935959]
 [ 0.03591165 -0.11786834  0.04123168 ...  0.06533947  0.11889934
  -0.19697346]
 ...
 [-0.05796075 -0.00695129  0.07784595 ... -0.03869986  0.10723818
   0.01293885]
 [ 0.13237114 -0.07055715 -0.05505611 ...  0.16433473  0.04535925
   0.0701588 ]
 [-0.2069717   0.04607181  0.07822093 ...  0.03704183  0.07326393
   0.06110878]]
Variable:  item_emb_w:0
Shape:  (1683, 20)
[[ 0.09130769 -0.16516572  0.06490657 ...  0.03657753 -0.02265425
   0.1437734 ]
 [ 0.02463264  0.13691436 -0.01713235 ...  0.02811887  0.00262074
   0.08854961]
 [ 0.00643777  0.02678963  0.04300125 ...  0.03529688 -0.11161
   0.11927075]
 ...
 [ 0.05260892 -0.03204868 -0.06910443 ...  0.03732759 -0.03459863
  -0.05798787]
 [-0.07953933 -0.10924194  0.11368059 ...  0.06346208 -0.03269136
  -0.03078123]
 [ 0.03460099 -0.10591184 -0.1008586  ... -0.07162578  0.00252131
   0.06791534]]

    现在我们已经得到了$W,H$矩阵,就可以对任意一个用户u的评分排序了。注意输出的$W,H$矩阵分别在values[0]和values[1]中。

    那么我们如何才能对某个用户推荐呢?这里我们以第一个用户为例,它在$W$中对应的$w_0$向量为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)

    输出为一个评分向量:

[[-0.01707731  0.06217583 -0.01760234 ...  0.067231    0.08989487
  -0.05628442]]

    现在给第一个用户推荐5部电影,代码如下:

print("以下是给用户0的推荐:")
p = numpy.squeeze(result_1)
p[numpy.argsort(p)[:-5]] = 0
for index in range(len(p)):
    if p[index] != 0:
        print (index, p[index])

    输出如下:

以下是给用户0的推荐:
54 0.1907271
77 0.17746378
828 0.17181025
1043 0.16989286
1113 0.17458326

4. 小结

    以上就是用tensorflow来构建BPR算法模型,并用该算法模型做movieLens 100K推荐的过程。实际做产品项目中,如果要用到BPR算法,一是要注意对隐藏维度k的调参,另外尽量多迭代一些轮数。

    另外我们可以在BPR损失函数那一块做文章。比如我们可以对$\overline{x}_{uij} = \overline{x}_{ui} - \overline{x}_{uj}$这个式子做改进,加上一个基于评分时间的衰减系数,这样我们的排序推荐还可以考虑时间等其他因素。

    以上就是用tensorflow学习BPR的全部内容。

(欢迎转载,转载请注明出处。欢迎沟通交流: [email protected])    

原文地址:https://www.cnblogs.com/pinard/p/9163481.html

时间: 2024-11-05 21:50:10

用tensorflow学习贝叶斯个性化排序(BPR)的相关文章

BPR贝叶斯个性化排序算法

原文地址:https://www.cnblogs.com/Lee-yl/p/9615043.html

PGM学习之六 从有向无环图(DAG)到贝叶斯网络(Bayesian Networks)

本文的目的是记录一些在学习贝叶斯网络(Bayesian Networks)过程中遇到的基本问题.主要包括有向无环图(DAG),I-Maps,分解(Factorization),有向分割(d-Separation),最小I-Maps(Minimal I-Maps)等.主要参考Nir Friedman的相关PPT. 1  概率分布(Probability Distributions) 令X1,...,Xn表示随机变量:令P是X1,...,Xn的联合分布(joint distribution).如果每

PGM学习之五 贝叶斯网络

本文的主题是"贝叶斯网络"(Bayesian Network) 贝叶斯网络是一个典型的图模型,它对感兴趣变量(variables of interest)及变量之间的关系(relationships)进行建模.当将贝叶斯模型与统计技术一起使用时,这种图模型分析数据具有如下几个优势: (1)    贝叶斯学习能够方便的处理不完全数据.例如考虑具有相关关系的多个输入变量的分类或回归问题,对标准的监督学习算法而言,变量间的相关性并不是它们处理的关键因素,当这些变量中有某个缺值时,它们的预测结

数据挖掘算法之贝叶斯网络

贝叶斯网络 序 上上周末写完上篇朴素贝叶斯分类后,连着上了七天班,而且有四天都是晚上九点下班,一直没有多少时间学习贝叶斯网络,所以更新慢了点,利用清明节两天假期,花了大概七八个小时,写了这篇博客,下面讲的例子有一个是上一篇朴素贝叶斯讲过的,还有其他的都是出自贝叶斯网络引论中.我会以通俗易懂的方式写出来,不会讲得很复杂,会介绍贝叶斯网络的绝大部分知识点,看完会让你对于贝叶斯网络有个大概的了解.但是对于比较深层次的东西,我先不打算写.比如训练贝叶斯网络,因为涉及到比较加深入的数学知识,我自己暂时也不

NLP系列(2)_用朴素贝叶斯进行文本分类(上)

作者:寒小阳 && 龙心尘 时间:2016年1月. 出处:http://blog.csdn.net/longxinchen_ml/article/details/50597149 http://blog.csdn.net/han_xiaoyang/article/details/50616559 声明:版权全部,转载请联系作者并注明出处 1. 引言 贝叶斯方法是一个历史悠久.有着坚实的理论基础的方法,同一时候处理非常多问题时直接而又高效.非常多高级自然语言处理模型也能够从它演化而来.因此,

课题论文之调研--贝叶斯网络

一:重要原理 (1)链规则: (2)贝叶斯定理: (3)变量间条件独立性: 二:主要问题 2.1贝叶斯网络概率推理 2.2结构学习:发现变量之间的图关系 结构学习算法: (1)K2算法: 通过为每个结点寻找父结点集合来学习贝叶斯网络结构.它不断往父结点集中添加结点,并选择能最大化数据和结构的联合概率的结点集. (2)HillClimbing (operators: edge addition, edge deletion, edge reversion) 从一个无边结构开始,在每一步,它添加能最

朴素贝叶斯算法的实例

贝叶斯的应用 过滤垃圾邮件 贝叶斯分类器的著名的应用就是垃圾邮件过滤了,这方面推荐想详细了解的可以去看看<黑客与画家>或是<数学之美>中对应的章节,贝叶斯的基础实现看这里 数据集 两个文件夹,分别是正常邮件和垃圾邮件,其中各有25封邮件 测试方法 从50封邮件中随机选取10封做为测试数据 实现细节 1.首先我们需要将文本转成我们需要的向量的样子,这里需要使用一点正则表达式2.由于采取交叉验证的方式,随机过程会导致每次的结果不尽相同 1 #coding=utf-8 2 from nu

02-NLP-02-朴素贝叶斯

朴素贝叶斯 1. 引言 贝叶斯方法是一个历史悠久,有着坚实的理论基础的方法,同时处理很多问题时直接而又高效,很多高级自然语言处理模型也可以从它演化而来.因此,学习贝叶斯方法,是研究自然语言处理问题的一个非常好的切入口. 2. 贝叶斯公式 贝叶斯公式就一行: P(Y|X)=P(X|Y)P(Y)P(X) 而它其实是由以下的联合概率公式推导出来: P(Y,X)=P(Y|X)P(X)=P(X|Y)P(Y) 其中P(Y)叫做先验概率,P(Y|X)叫做后验概率,P(Y,X)叫做联合概率. 没了,贝叶斯最核心

概率图模型学习笔记(二)贝叶斯网络-语义学与因子分解

概率分布(Distributions) 如图1所示,这是最简单的联合分布案例,姑且称之为学生模型. 图1 其中包含3个变量,分别是:I(学生智力,有0和1两个状态).D(试卷难度,有0和1两个状态).G(成绩等级,有1.2.3三个状态). 表中就是概率的联合分布了,表中随便去掉所有包含某个值的行,就能对分布表进行缩减. 例如可以去掉所有G不为1的行,这样就只剩下了1.4.7.10行,这样他们的概率之和就不为1了,所以可以重新标准化(Renormalization).如图2所示. 图2 反之也可以