# 【推荐系统入门到项目实践】(四):Baseline和Slope one算法

简介: # 【推荐系统入门到项目实践】(四):Baseline和Slope one算法

【推荐系统入门到项目实践】(四):Baseline和Slope one算法


  • 🌸个人主页:JOJO数据科学
  • 📝个人介绍:统计学top3高校统计学硕士在读
  • 💌如果文章对你有帮助,欢迎✌关注、👍点赞、✌收藏、👍订阅专栏
  • ✨本文收录于【推荐系统入门到项目实战】本系列主要分享一些学习推荐系统领域的方法和代码实现。

@[TOC]

引言

在上一篇文章中我们介绍了矩阵分解和ALS算法,上面这种方法的一个缺点是只使用了个性化推荐部分,没有考虑到物品和用户本身的整体情况。因此,下面介绍介绍两种用于反映一个整体情况推荐的方法。

baseline算法

基本原理

Baseline算法:基于统计的基准预测线打分

$b_{ui}$ 预测值

$b_u$ 用户对整体的偏差

$b_i$ 商品对整体的偏差

$$ b_{ui}=u+b_{u}+b_i $$

以对泰坦尼克号评分为例,其中$b_{ui}$指的是 预测值,$u$指的是所有电影的 平均得分,$b_{u}$表示的是 泰坦尼克号比其他电影平均多的评分(如果是负数则少的评分),$b_i$指的是 用户的评分误差,假设他比较苛刻,可能比其他用户平均少打的分数

使用ALS来估计Baseline算法

Step1,固定bu,优化bi

Step2,固定bi,优化bu

image-20220803113938941

实现方法

我们使用Surprise库来实现(https://surprise.readthedocs.io/en/stable/

surprise包含了许多推荐算法的实现方法。常用的几种方法如下:

算法 描述
NormalPredictor() 基于统计的推荐系统预测打分,假定用户打分的分布是基于正态分布的
BaselineOnly 基于统计的基准预测线打分
knns.KNNBasic 基本的协同过滤算法
knns.KNNWithMeans 协同过滤算法的变种,考虑每个用户的平均评分
knns.KNNWithZScore 协同过滤算法的变种,考虑每个用户评分的归一化操作
knns.KNNBaseline 协同过滤算法的变种,考虑每个用户评分的基线
matrix_factorzation.SVD SVD 矩阵分解算法
matrix_factorzation.SVDpp SVD++ 矩阵分解算法
matrix_factorzation.NMF 一种非负矩阵分解的协同过滤算法
SlopeOne SlopeOne 协同过滤算法

其中baseline算法包含两个主要的方法NormalPredictorBaselineOnly

Normal Perdictor 认为用户对物品的评分是服从正态分布的,从而可以根据已有的评分的均值和方差预测当前用户对其他物品评分的分数。

BaselineOnly算法的思想就是设立基线,并引入用户的偏差以及item的偏差

image-20221022143247534

μ为所有用户对电影评分的均值

$b_{ui}$:待求的基线模型中用户u给物品i打分的预估值

$b_u$:user偏差(如果用户比较苛刻,打分都相对偏低, 则bu<0;反之,bu>0);

$b_i$:item偏差,反映商品受欢迎程度

评估方法

K折交叉验证

将训练集数据划分为K份,使用其中的K-1份作为训练集,剩余一份作为测试集

K次误差的平均值作为泛化误差

所有数据都做过训练和测试,更好的利用数据

K越大,平均误差作为泛化误差的结果就越可靠,但花费的时间也时间也越长

image-20221022143444226

Surprise中的评价指标

RMSE:均方根误差

image-20221022144133214

MAE:平均绝对误差

image-20221022144138464

下面我们以RMSE为评估指标,使用baselines算法对评分数据集进行预测。

代码实现

from surprise import Dataset
from surprise import Reader
from surprise import BaselineOnly, KNNBasic, NormalPredictor
from surprise import accuracy
from surprise.model_selection import KFold
#import pandas as pd

# 数据读取
reader = Reader(line_format='user item rating timestamp', sep=',', skip_lines=1)
data = Dataset.load_from_file('MovieLens/ratings.csv', reader=reader)
train_set = data.build_full_trainset()

# ALS优化
bsl_options = {'method': 'als','n_epochs': 5,'reg_u': 12,'reg_i': 5}
# SGD优化
#bsl_options = {'method': 'sgd','n_epochs': 5}
algo = BaselineOnly(bsl_options=bsl_options)
#algo = BaselineOnly()
#algo = NormalPredictor()#大家也可以试一下这个方法

# 定义K折交叉验证迭代器,K=3
kf = KFold(n_splits=3)
for trainset, testset in kf.split(data):
    # 训练并预测
    algo.fit(trainset)
    predictions = algo.test(testset)
    # 计算RMSE
    accuracy.rmse(predictions, verbose=True)

uid = str(196)
iid = str(302)
# 输出uid对iid的预测结果
pred = algo.predict(uid, iid, r_ui=4, verbose=True)



Estimating biases using als...
RMSE: 0.8635
Estimating biases using als...
RMSE: 0.8644
Estimating biases using als...
RMSE: 0.8636
user: 196        item: 302        r_ui = 4.00   est = 4.07   {'was_impossible': False}

est为我们的预测值,$r_{ui}$为真实值。可以看出预测的结果还不错,但是其实这种方法没有利用到用户和物品之间的交互信息,更多的是从一个某个用户或者某个商品的总体情况来进行预测,当数据质量较好时,这种方法的效果还不错。下面我们再来介绍一种更简单的方法。

SlopeOne算法

基本原理

Slope One 是一系列应用于协同过滤的算法的统称。由 Daniel LemireAnna Maclachlan于2005年发表的论文中提出。有争议的是,该算法堪称基于项目评价的协同过滤算法最简洁的形式。该系列算法的简洁特性使它们的实现简单而高效,而且其精确度与其它复杂费时的算法相比也不相上下。 该系列算法也被用来改进其它算法。它最大优点在于算法很简单, 易于实现, 效率高且推荐准确度较高

为什么说他简单呢?因为在本质上,它其实是运用了一个回归表达式$f(x)=x+b$,其中只有一个单一的自由参数b。我想这也是它的名字来源吧,slope我们知道是斜率的意思,slope one也就是斜率为1。该自由参数只不过就是两个项目评分间的平均差值。甚至在某些实例当中,它比线性回归的方法更准确,而且该算法只需要一半(甚至更少)的存储量。

下面我们来看看具体是怎么计算的。

用户 商品1评分 商品2
A 5 3
B 4 3
C 4 ?

我们现在有三个用户对两个商品的评分矩阵,如上表所示,但是c对商品2的评分我们是未知的,那么我们怎么估计它呢?

我们根据A,B用户对两个商品的评分差值来进行估计。

用户A:商品1-商品2=(5-3)=2

用户B:商品1-商品2=4-3=1

那么我们认为商品1平均比商品2评分要高(1+2)/2=1.5。那么我们就可以计算出用户C对商品2的评分

C对商品2的评分=4-((5-3)+(4-3))/2=2.5

那么我们现在来看看slopeOne算法的一般步骤

Step1,计算Item之间的评分差的均值,记为评分偏差(两个item都评分过的用户)

image-20221022151649334

Step2,根据Item间的评分偏差和用户的历史评分,预测用户对未评分的item的评分

Step3,将预测评分排序,取topN对应的item推荐给用户

下面我们来看一个更复杂的具体的案例:

a b c d
A 5 3.5
B 2 5 4 2
C 4.5 3.5 1 4

我们要补全上面这个评分矩阵

Step1,计算Item之间的评分差的均值

b与a:((3.5-5)+(5-2)+(3.5-4.5))/3=0.5/3

c与a:((4-2)+(1-4.5))/2=-1.5/2

d与a:((2-2)+(4-4.5))/2=-0.5/2

c与b:((4-5)+(1-3.5))/2=-3.5/2

d与b:((2-5)+(4-3.5))/2=-2.5/2

d与c:((2-4)+(4-1))/2=1/2

将上面计算结果写成矩阵的形式,如下所示,我们称为评分偏差矩阵,以0.17为例,表示商品b比a平均高0.17分

a b c d
a
b 0.17
c -0.75 -1.75
d -0.25 -1.25 0.5

Step2,预测用户A对商品c和d的评分

A对c评分=((-0.75+5)+(-1.75+3.5))/2=3

A对d评分=((-0.25+5)+(-1.25+3.5))/2=3.5

a b c d
A 5 3.5 3 3.5
B 2 5 4 2
C 4.5 3.5 1 4

Step3,将预测评分排序,推荐给用户

推荐顺序为{d, c}

加权算法 Weighted Slope One

如果有100个用户对Item1和Item2都打过分, 有1000个用户对Item3和Item2也打过分,显然这两个rating差的权重是不一样的,因此计算方法为:

(100*(Rating 1 to 2) + 1000(Rating 3 to 2)) / (100 + 1000)

SlopeOne算法的特点

适用于item更新不频繁,数量相对较稳定

item数<<user数

算法简单,易于实现,执行效率高

依赖用户行为,存在冷启动问题和稀疏性问题

用户 商品1评分 商品2
X 5 3
Y 4 3
Z 4 ?

代码实现

from surprise import SVD
from surprise import Dataset
from surprise.model_selection import cross_validate
#from surprise import evaluate, print_perf
from surprise import Reader
from surprise import BaselineOnly, KNNBasic, KNNBaseline, SlopeOne
from surprise import accuracy
from surprise.model_selection import KFold
import pandas as pd
import io
import pandas as pd

# 读取物品(电影)名称信息
def read_item_names():
    file_name = ('./movies.csv') 
    data = pd.read_csv('./movies.csv')
    rid_to_name = {}
    name_to_rid = {}
    for i in range(len(data['movieId'])):
        rid_to_name[data['movieId'][i]] = data['title'][i]
        name_to_rid[data['title'][i]] = data['movieId'][i]

    return rid_to_name, name_to_rid 

# 数据读取
reader = Reader(line_format='user item rating timestamp', sep=',', skip_lines=1)
data = Dataset.load_from_file('MovieLens/ratings.csv', reader=reader)
train_set = data.build_full_trainset()


# 使用SlopeOne算法
algo = SlopeOne()
algo.fit(train_set)
# 对指定用户和商品进行评分预测
uid = str(196) 
iid = str(302) 
pred = algo.predict(uid, iid, r_ui=4, verbose=True)
user: 196        item: 302        r_ui = 4.00   est = 4.32   {'was_impossible': False}

可以看出,使用slopeone算法得到的预测结果为4.32,效果似乎相对于baseline算法要差一些,其实这也不难猜到,因为我们毕竟用的是一个太简单的模型。但是他对于数据量相对没那么敏感

总结

介绍了baseline算法slopeOne算法。

baseline算法的核心思想是将用户评分分解成三个部分

  • $u$:平均评分
  • $b_u$:用户评分偏差
  • $b_i$:物品评分偏差

然后使用ALS方法进行求解

SlopeOne算法的核心思想根据用户对不同商品评分的差异来预测某一商品的评分。大家记得那个具体的案例即可。

下一章我们将讨论SVD矩阵分解以及一些拓展的模型。

相关文章
|
2天前
|
算法 前端开发 Android开发
Android文字基线Baseline算法的使用讲解,Android开发面试题
Android文字基线Baseline算法的使用讲解,Android开发面试题
Android文字基线Baseline算法的使用讲解,Android开发面试题
|
4天前
|
算法 搜索推荐 数据挖掘
MATLAB模糊C均值聚类FCM改进的推荐系统协同过滤算法分析MovieLens电影数据集
MATLAB模糊C均值聚类FCM改进的推荐系统协同过滤算法分析MovieLens电影数据集
|
4天前
|
存储 机器学习/深度学习 算法
|
4天前
|
机器学习/深度学习 人工智能 算法
分类算法入门:以鸢尾花数据集为例(上)
分类算法入门:以鸢尾花数据集为例(上)
37 2
|
4天前
|
机器学习/深度学习 算法 数据可视化
分类算法入门:以鸢尾花数据集为例(下)
分类算法入门:以鸢尾花数据集为例(下)
57 2
|
4天前
|
存储 算法 JavaScript
Java入门高频考查算法逻辑基础知识3-编程篇(超详细18题1.8万字参考编程实现)
解决这类问题时,建议采取下面的步骤: 理解数学原理:确保你懂得基本的数学公式和法则,这对于制定解决方案至关重要。 优化算法:了解时间复杂度和空间复杂度,并寻找优化的机会。特别注意避免不必要的重复计算。 代码实践:多编写实践代码,并确保你的代码是高效、清晰且稳健的。 错误检查和测试:要为你的代码编写测试案例,测试标准的、边缘情况以及异常输入。 进行复杂问题简化:面对复杂的问题时,先尝试简化问题,然后逐步分析和解决。 沟通和解释:在编写代码的时候清晰地沟通你的思路,不仅要写出正确的代码,还要能向面试官解释你的
36 0
|
4天前
|
存储 算法 JavaScript
【C++ 泛型编程 入门篇】 C++ 中的泛型算法 STL(sort,find)(二)
【C++ 泛型编程 入门篇】 C++ 中的泛型算法 STL(sort,find)
35 0
|
4天前
|
算法 数据安全/隐私保护 计算机视觉
基于二维CS-SCHT变换和LABS方法的水印嵌入和提取算法matlab仿真
该内容包括一个算法的运行展示和详细步骤,使用了MATLAB2022a。算法涉及水印嵌入和提取,利用LAB色彩空间可能用于隐藏水印。水印通过二维CS-SCHT变换、低频系数处理和特定解码策略来提取。代码段展示了水印置乱、图像处理(如噪声、旋转、剪切等攻击)以及水印的逆置乱和提取过程。最后,计算并保存了比特率,用于评估水印的稳健性。
|
1天前
|
算法
m基于BP译码算法的LDPC编译码matlab误码率仿真,对比不同的码长
MATLAB 2022a仿真实现了LDPC码的性能分析,展示了不同码长对纠错能力的影响。短码长LDPC码收敛快但纠错能力有限,长码长则提供更强纠错能力但易陷入局部最优。核心代码通过循环进行误码率仿真,根据EsN0计算误比特率,并保存不同码长(12-768)的结果数据。
19 9
m基于BP译码算法的LDPC编译码matlab误码率仿真,对比不同的码长
|
2天前
|
算法
MATLAB|【免费】融合正余弦和柯西变异的麻雀优化算法SCSSA-CNN-BiLSTM双向长短期记忆网络预测模型
这段内容介绍了一个使用改进的麻雀搜索算法优化CNN-BiLSTM模型进行多输入单输出预测的程序。程序通过融合正余弦和柯西变异提升算法性能,主要优化学习率、正则化参数及BiLSTM的隐层神经元数量。它利用一段简单的风速数据进行演示,对比了改进算法与粒子群、灰狼算法的优化效果。代码包括数据导入、预处理和模型构建部分,并展示了优化前后的效果。建议使用高版本MATLAB运行。