当前位置:网站首页>BPR(贝叶斯个性化排序)
BPR(贝叶斯个性化排序)
2022-07-01 19:17:00 【qq_53430308】
1.什么是BPR以及他产生的背景
BPR全称Bayesian Personalized Ranking,他是一种排序算法,并且使用隐式反馈(如点击,收藏等),通过对问题进行贝叶斯分析得到的最大后验概率来对item进行排序,进而产生推荐。
传统的矩阵分解使用显示反馈通过对用户-物品的评分矩阵进行分解从而预测到用户对于未评分物品的得分,根据这个得分进行推荐。
在实际中显示反馈有着较高的准确率,但它往往难以收集,我们有时候只能使用隐式反馈,它可以通过日志文件很方便的得到。显示反馈和隐式反馈的特点如下:
因为我们有时候只能使用隐式反馈所以传统的矩阵分解在这里就很难起作用,这个时候就该BPR登场了。
2.BPR算法的原理
2.1算法的思想和参数
BPR中使用的训练集是一个三元组<u,i,j>,他的意思是说对于用户u来说物品i的排名要比j靠前,也可以用 i >u j 来表示。
BPR基于贝叶斯他有两个假设:
1. 一是每个用户之间的偏好行为相互独立,即用户u在商品i和j之间的偏好和其他用户无关。
2. 二是同一用户对不同物品的偏序相互独立,也就是用户u在商品i和j之间的偏好和其他的商品无关。
在BPR中排序关系符号>u满足完全性,反对称性和传递性,即对于用户集U和物品集I:
完整性:对于两个不同的物品一定存在排序关系.。
反对称性:如果两个物品之间的排序关系的位置可以调换,那么他们是同一间物品。
传递性:如果用户u喜欢i大于j并且喜欢j大于k那么用户u喜欢i大于k。
BPR做为一种排序算法它能应用于许多模型之中,比如说KNN和矩阵分解,这里我们以矩阵分解为例子。
在矩阵分解中我们分解的是用户对于物品的评分矩阵,那么在这里我们要分解的是用户对于物品的排序分的矩阵。
BPR中用户集U和物品集I对应的U×I的预测排序矩阵 ,我们把这个矩阵分解得到两个矩阵,用户矩阵W(|U|×k),物品矩阵H(|I|×k),并且满足:
那么对于每一个用户来说:
我们的目的就是通过某种优化方法找到其中的最佳W和H矩阵,矩阵分解用的是全体用户的平方损失,而BPR则从概率的角度给出了优化方法。
2.2BPR优化的思路
BPR基于最大后验概率估计 来求解模型参数W,H ,我们用θ来表示参数W,H,>u代表用户u对所有商品的全序关系,优化目标是
,即我们要让这个概率最大,根据贝叶斯公式我们可以得到:
因为我们假设了用户的排序和其他用户无关, 所以对于任意一个用户u来说P(>u)是一个常数,所以P(θ|>u)正比于P(>u|θ)P(θ)。
此时我们就将要求解的式子分成了两部分,第一部分和样本数据集D有关,第二部分和样本数据集无关。对于第一部分,由于我们假设每个用户之间的偏好行为相互独立,同一用户对不同物品的偏序相互独立,所以有:
if b is true
else
根据反对称性和完整性:
我们又可以做如下替换:
用sigmoid函数来替换这个概率, 不仅满足了BPR的三条性质而且便于计算。
对于当满足i>uj 时其值大于0,反之其值小于0因此我们可以用下式来表示:
当然不一定必须表示成减法的形式,只要满足i>uj时的值大于0,以及相反的条件即可。
最终我们的第一部分就转化成了:
对于P(θ),原论文中作者使用了贝叶斯假设,即这个概率符合正态分布且对应的均值为0,协方差矩阵是 即:
原作者这么假设是因为我们后面要计算lnP(θ) 而lnP(θ)和||θ||^2成正比,即:
所以最终我们就可以得到:
我们可以用梯度下降法对参数进性更新,我们一共有三个参数分别为,求导后得到:
因为:
于是当θ分别为 时上式的对θ的求导结果分别为:
,
,
,这样我们就可以进行参数的更新。
3.个人对BPR的理解(仅供参考)
1.BPR算法的新奇之处在于它以不同的角度提出了一种优化思路,它不再使用用户的评分这种显示反馈,而是使用用户是否对该物品有过行为这种隐式反馈。
2.基于矩阵分解的BPR初始化的矩阵为预测分矩阵,最终更新的结果也是一个预测分矩阵,推荐时选取一行,即一个用户对应的物品的预测分,根据这个分数进行推荐。
BPR代码可以在参考文章中找到,而且很多函数原作者已经给了注释,这里只对其做大概的解释,所以只给出主函数:
# !/usr/bin/env python
# @Time:2021/4/6 19:21
# @Author:华阳
# @File:Basical BPR.py
# @Software:PyCharm
import random
from collections import defaultdict
import numpy as np
from sklearn.metrics import roc_auc_score
import scores
'''
函数说明:BPR类(包含所需的各种参数)
Parameters:
无
Returns:
无
'''
class BPR:
#用户数
user_count = 943
#项目数
item_count = 1682
#k个主题,k数
latent_factors = 20
#步长α
lr = 0.01
#参数λ
reg = 0.01
#训练次数
train_count = 1
#训练集
train_data_path = 'train.txt'
#测试集
test_data_path = 'test.txt'
#U-I的大小
size_u_i = user_count * item_count
# 随机设定的U,V矩阵(即公式中的Wuk和Hik)矩阵
U = np.random.rand(user_count, latent_factors) * 0.01 #大小无所谓 类型为numpy.ndarray
V = np.random.rand(item_count, latent_factors) * 0.01
#print(np.mat(U)*np.mat(V).T)
biasV = np.random.rand(item_count) * 0.01 #大小为1行item_count列
#生成一个用户数*项目数大小的全0矩阵
test_data = np.zeros((user_count, item_count))
print("test_data_type",type(test_data))
#生成一个一维的全0矩阵
test = np.zeros(size_u_i)
#再生成一个一维的全0矩阵
predict_ = np.zeros(size_u_i)
#获取U-I数据对应
'''
函数说明:通过文件路径,获取U-I数据
Paramaters:
输入要读入的文件路径path
Returns:
输出一个字典user_ratings,包含用户-项目的键值对
'''
def load_data(self, path):
user_ratings = defaultdict(set)
with open(path, 'r') as f: #with as 先执行open类,将里边的_enter_函数的返回值赋给 f 再执行结构内的语句 无论成功失败都会执行open类中的_exit_函数
for line in f.readlines():
u, i = line.split(" ")
u = int(u)
i = int(i)
user_ratings[u].add(i)
return user_ratings
'''
函数说明:通过文件路径,获取测试集数据
Paramaters:
测试集文件路径path
Returns:
输出一个numpy.ndarray文件(n维数组)test_data,其中把含有反馈信息的数据置为1
'''
#获取测试集的评分矩阵
def load_test_data(self, path):
file = open(path, 'r')
for line in file:
line = line.split(' ')
user = int(line[0])
item = int(line[1])
self.test_data[user - 1][item - 1] = 1 #test_data大小为user_count*item_count,其中用户有交互的项目值为1,其余的值为0
#print(self.test_data)
'''
函数说明:对训练集数据字典处理,通过随机选取,(用户,交互,为交互)三元组,更新分解后的两个矩阵
Parameters:
输入要处理的训练集用户项目字典
Returns:
对分解后的两个矩阵以及偏置矩阵分别更新
'''
def train(self, user_ratings_train): #user_ratings_train为一个字典
for user in range(self.user_count):
# 随机获取一个用户
u = random.randint(1, self.user_count) #找到一个user 返回1到user_count之间的任意一个数,闭区间
# 训练集和测试集的用于不是全都一样的,比如train有948,而test最大为943
if u not in user_ratings_train.keys():
continue
# 从用户的U-I中随机选取1个Item
i = random.sample(user_ratings_train[u], 1)[0] #找到一个item,被评分
#print(i)
# 随机选取一个用户u没有评分的项目
j = random.randint(1, self.item_count)
while j in user_ratings_train[u]: #当j在其中时说明评分了,重新寻找
j = random.randint(1, self.item_count) #找到一个item,没有被评分
#构成一个三元组(uesr,item_have_score,item_no_score)
# python中的取值从0开始
u = u - 1
i = i - 1
j = j - 1
#BPR
r_ui = np.dot(self.U[u], self.V[i].T) + self.biasV[i]
r_uj = np.dot(self.U[u], self.V[j].T) + self.biasV[j]
r_uij = r_ui - r_uj
loss_func = -1.0 / (1 + np.exp(r_uij))
# 更新2个矩阵
self.U[u] += -self.lr * (loss_func * (self.V[i] - self.V[j]) + self.reg * self.U[u])
self.V[i] += -self.lr * (loss_func * self.U[u] + self.reg * self.V[i])
self.V[j] += -self.lr * (loss_func * (-self.U[u]) + self.reg * self.V[j])
# 更新偏置项
self.biasV[i] += -self.lr * (loss_func + self.reg * self.biasV[i])
self.biasV[j] += -self.lr * (-loss_func + self.reg * self.biasV[j])
'''
函数说明:通过输入分解后的用户项目矩阵得到预测矩阵predict
Parameters:
输入分别后的用户项目矩阵
Returns:
输出相乘后的预测矩阵,即我们所要的评分矩阵
'''
def predict(self, user, item):
predict = np.mat(user) * np.mat(item.T)
return predict
#主函数
def main(self):
#获取U-I的{1:{2,5,1,2}....}数据
user_ratings_train = self.load_data(self.train_data_path)
#print(user_ratings_train)
#获取测试集的评分矩阵
self.load_test_data(self.test_data_path)
#将test_data矩阵拍平
for u in range(self.user_count):
for item in range(self.item_count):
if int(self.test_data[u][item]) == 1:
self.test[u * self.item_count + item] = 1
else:
self.test[u * self.item_count + item] = 0
#训练
for i in range(self.train_count):
self.train(user_ratings_train) #训练10000次完成
predict_matrix = self.predict(self.U, self.V) #将训练完成的矩阵內积
#print(predict_matrix)
# 预测
self.predict_ = predict_matrix.getA().reshape(-1) #.getA()将自身矩阵变量转化为ndarray类型的变量 reshape(-1)表示将他转化成1行不知道多少列的矩阵
print("predict_new",self.predict_)
self.predict_ = pre_handel(user_ratings_train, self.predict_, self.item_count)
auc_score = roc_auc_score(self.test, self.predict_)
print('AUC:', auc_score)
# Top-K evaluation
scores.topK_scores(self.test, self.predict_, 5, self.user_count, self.item_count)
'''
函数说明:对结果进行修正,即用户已经产生交互的用户项目进行剔除,只保留没有产生用户项目的交互的数据
Paramaters:
输入用户项目字典集,以及一维的预测矩阵,项目个数
Returns:
输出修正后的预测评分一维的预测矩阵
'''
def pre_handel(set, predict, item_count):
# Ensure the recommendation cannot be positive items in the training set.
for u in set.keys():
for j in set[u]:
predict[(u - 1) * item_count + j - 1] = 0
return predict
if __name__ == '__main__':
#调用类的主函数
bpr = BPR()
bpr.main()
先说一下数据集,有两个部分,训练集和测试集,都是有两列,第一列代表用户的代号,第二例是物品的代号。
首先引入各种库和初始化参数,其中初始的U和V两个矩阵是排序分的矩阵也就是文中的W和H,biasV是偏置项的矩阵。
train函数里的参数更新方式对比一下是和理论中的一致的,每次循环只选取一个用户和它的一个有交互的物品和一个没有交互的物品进行参数更新,这也就是在成千上万的物品中选取一个看它和当前的物品相比谁更能得到用户的喜爱。
4.参考文章
贝叶斯个性化排序(BPR)算法小结 - 刘建平Pinard - 博客园
BPR贝叶斯个性化推荐算法—推荐系统基础算法(含python代码实现以及详细例子讲解)_啥都不懂的小程序猿的博客-CSDN博客_贝叶斯推荐
本文只是个人在学习过程中总结的以上两篇文章的内容,和自己的一些看法,仅供参考。
边栏推荐
- 收藏:存储知识全面总结
- How to prevent repeated submission of new orders
- Arduino stepper library drive 28byj-48 stepper motor test program
- 《軟件工程導論(第六版)》 張海藩 複習筆記
- Review notes of Zhang Haifan in introduction to software engineering (Sixth Edition)
- internship:复杂json格式数据编写接口
- 8K HDR!| Hevc hard solution for chromium - principle / Measurement Guide
- 深度学习 神经网络基础
- 2022年低压电工考试试题及答案
- 【let var const】
猜你喜欢
Flask 常用组件
[Mysql]安装Mysql5.7
《软件工程导论(第六版)》 张海藩 复习笔记
基于图的 Affinity Propagation 聚类计算公式详解和代码示例
[mysql] install mysql5.7
Myslq ten kinds of locks, an article will take you to fully analyze
考虑关系的图卷积神经网络R-GCN的一些理解以及DGL官方代码的一些讲解
Getting started with fastdfs
RichView 文档中的 ITEM
RichView RichEdit SRichViewEdit PageSize 页面设置与同步
随机推荐
利用QEventLoop实现同步等待槽函数返回
8K HDR!|为 Chromium 实现 HEVC 硬解 - 原理/实测指南
Understand the structure in C language in one article
关于一个神奇函数的用法
薛定谔的日语学习小程序源码
宅男壁纸大全微信小程序源码-带动态壁纸支持多种流量主
Iframe 父子页面通信
【Leetcode】最大连续1的个数
C#联合halcon应用——大华相机采集类
走进如心小镇,数智化变革连接“未来社区”
图片拼图微信小程序源码_支持多模板制作和流量主
喜马拉雅自研网关架构演进过程
Set object value changes null value object
C#聯合halcon應用——大華相機采集類
C # joint Halcon application - Dahua camera acquisition class
3D panoramic model display visualization technology demonstration
#yyds干货盘点#SQL聚合查询方法总结
STC 32-bit 8051 single chip microcomputer development example tutorial II i/o working mode and its configuration
目標檢測——Yolo系列
Getting started with fastdfs