大数据——协同过滤推荐算法:线性回归算法

推荐系统中的协同过滤算法一般分为两大类:

  • 基于行为的协同过滤算法(Memory-Based CF),利用用户行为数据计算相似度,包括用户之间的相似度和物品之间的相似度。
  • 基于模型的协同过滤算法(Model-Based CF),利用机器学习算法预测用户的喜好程度,一般用户数据较为稀疏的时候更适合这种方法。

本文主要介绍Model-Based协同过滤算法

1、Model-Based CF基于模型协同过滤算法

利用用户-物品评分矩阵训练机器学习模型,从而预测用户对物品的评分,主要可以分为以下几类:

  • 基于分裂、回归或者聚类算法
  • 基于矩阵分解的推荐算法
  • 基于神经网络算法
  • 基于图模型算法

2、基于回归模型算法的协同过滤

回归模型的前提是连续的值,我们将评分看做连续的值,采用以下Baseline(基准预测)实现策略。其思想是运用每个人的偏好不同:

有些用户比较好心,他的评分高于其他用户,有些用户比较苛刻,他的评分低于其他用户;而部分物品比较受欢迎,它的评分高于一般物品,部分物品可能会被嫌弃,它的评分会低于一般物品。

而Baseline则是通过找出每个用户与其他用户的评分偏置值 b u b_u bu,每个物品与其他物品的偏置值 b i b_i bi,最终的目标也就变成了寻找最优的 b u b_u bu b i b_i bi。所以Baseline算法的步骤如下:

  1. 计算所有电影的平均评分 u u u;
  2. 计算每个用户的评分与平均评分的偏置值 b u b_u bu;
  3. 计算每部电影的评分与平均评分的偏置值 b i b_i bi
  4. 预测用户对电影的评分:
    r ^ u i = b u i = u + b u + b i \hat{r}_{ui} = b_{ui} = u+b_u+b_i r^ui=bui=u+bu+bi

以用户A对《封神第一部》的评分为例:

  1. 首先计算所有电影的平均评分 u = 3.5 u=3.5 u=3.5
  2. 用户A比较好心,普遍比平均分高1分,偏置值 b u = 1 b_u=1 bu=1
  3. 《封神第一部》一开始差评比较多,评分比平均分低0.5分,偏置值 b i = − 0.5 b_i=-0.5 bi=0.5
  4. 则用户A对《封神第一部》的评分为:3.5+1-0.5=4.1分。

在线性回归问题中,我们用平方差构建损失函数:
C o s t = ∑ u , i ∈ R ( r u i − r ^ u i ) 2 = ∑ u , i ∈ R ( r u i − u − b u − b i ) 2 Cost = \sum_{u,i∈R}(r_{ui}-\hat{r}_{ui})^2 = \sum_{u,i∈R}(r_{ui}-u-b_u-b_i)^2 Cost=u,iR(ruir^ui)2=u,iR(ruiububi)2
为了防止过拟合,需要加入L2范式,最后的公示如下:
C o s t = ∑ u , i ∈ R ( r u i − u − b u − b i ) 2 + λ ( ∑ u b u 2 + ∑ i b i 2 ) Cost = \sum_{u,i∈R}(r_{ui}-u-b_u-b_i)^2 + \lambda(\sum_u{b_u}^2+\sum_i{b_i}^2) Cost=u,iR(ruiububi)2+λ(ubu2+ibi2)
我们希望得到损失函数的最小值,一般会采用随机梯度下降法或者最小二乘法来优化实现。

2.1 Baseline随机梯度下降法算法

step1:梯度下降法推导:
J ( θ ) = f ( b u , b i ) J(θ) = f(b_u,b_i) J(θ)=f(bu,bi)
梯度下降参数更新的原始公式:
θ j : = θ j − α ∂ ∂ θ j J ( θ ) \theta_j :=\theta_j-\alpha\frac{∂}{∂\theta_j}J(\theta) θj:=θjαθjJ(θ)
对参数求偏导:
∂ ∂ b u J ( θ ) = ∂ ∂ b u f ( b u , b i ) = − 2 ∑ u , i ∈ R ( r u i − u − b u − b i ) + 2 λ b u \frac{∂}{∂b_u}J(\theta) = \frac{∂}{∂b_u}f(b_u,b_i) = -2\sum_{u,i∈R}(r_{ui}-u-b_u-b_i) + 2\lambda b_u buJ(θ)=buf(bu,bi)=2u,iR(ruiububi)+2λbu
代入梯度下降参数更新公式:
b u : = b u + α ( ∑ u , i ∈ R ( r u i − u − b u − b i ) − λ b u ) b_u:=b_u+\alpha(\sum_{u,i∈R}(r_{ui}-u-b_u-b_i) -\lambda b_u) bu:=bu+α(u,iR(ruiububi)λbu)
b i : = b i + α ( ∑ u , i ∈ R ( r u i − u − b u − b i ) − λ b i ) b_i:=b_i+\alpha(\sum_{u,i∈R}(r_{ui}-u-b_u-b_i) -\lambda b_i) bi:=bi+α(u,iR(ruiububi)λbi)
step2:随机梯度下降
随机梯度下降法本质上是用每个样本的损失来更新参数,不用每次求出全部的损失和。
单样本损失值:
e r r o r = r u i − r ^ u i = r u i − u − b u − b i error = r_{ui} - \hat{r}_{ui} = r_{ui} - u-b_u-b_i error=ruir^ui=ruiububi
所以梯度下降公式可以更新为:
b u : = b u + α ( e r r o r − λ b u ) b_u:=b_u+\alpha(error -\lambda b_u) bu:=bu+α(errorλbu)
b i : = b i + α ( e r r o r − λ b i ) b_i:=b_i+\alpha(error -\lambda b_i) bi:=bi+α(errorλbi)
step3:算法实现
导入模块和数据

# 随机梯度下降算法实现
import pandas as pd
import numpy as np
df = pd.read_csv("ml-latest-small/ratings.csv", usecols=range(3))
df

Baseline梯度下降算法实现

class BaselineCFBySGD(object):'''max_epochs 梯度下降迭代次数alpha 学习率reg 过拟合参数columns 数据字段名称'''def __init__(self,max_epochs, alpha,reg,columns=['uid','mid','rating']):self.max_epochs = max_epochsself.alpha = alphaself.reg = regself.columns = columnsdef fit(self,data):''':param data:uid,mid,rating:return:'''self.data = data# 用户评分数据self.users_rating = data.groupby(self.columns[0]).agg([list])[[self.columns[1], self.columns[2]]]# 电影评分数据self.items_rating = data.groupby(self.columns[1]).agg([list])[[self.columns[0], self.columns[2]]]# 全局平均分self.global_mean = self.data[self.columns[2]].mean()# 调用随机梯度下降训练模型参数self.bu,self.bi = self.sgd()def sgd(self):'''随机梯度下降,优化bu和bi值:return: bu bi'''bu = dict(zip(users_rating.index, np.zeros(len(users_rating))))bi = dict(zip(items_rating.index, np.zeros(len(items_rating))))for i in range(max_epochs):# 将dataframe的每一行数据单独读出来,代入梯度下降参数公式for uid, mid, real_rating in df.itertuples(index=False):error = real_rating - (global_mean+bu[uid]+bi[mid])bu[uid] += alpha*(error - reg*bu[uid])bi[mid] += alpha*(error - reg*bi[mid])return bu,bidef predict(self,uid,mid):'''使用评分公式进行预测param uid,mid;return predict_rating;'''predict_rating = self.global_mean+self.bu[uid]+self.bi[mid]return predict_ratingdef test(self,testset):'''使用预测函数预测测试集数据param testset;return yield;'''for uid,mid,real_rating in testset.itertuples(index=False):try:# 使用predict函数进行预测pred_rating = self.predict(uid,mid)except Exception as e:print(e)else:# 返回生成器对象yield uid,mid,real_rating,pred_rating

测试集和训练集划分函数

# 训练集和测试集的划分
def data_split(data_path, x=0.8, random=False):ratings = pd.read_csv(data_path, usecols=range(3))testset_index = []for uid in ratings.groupby('userId').any().index:user_rating_data = ratings.where(ratings['userId']==uid).dropna()if random:index = list(user_rating_data.index)np.random.shuffle(index)_index = round(len(user_rating_data)*x)testset_index += list(index[_index:])else:index = round(len(user_rating_data)*x)testset_index += list(user_rating_data.index.values[index:])testset = ratings.loc[testset_index]trainset = ratings.drop(testset_index)return trainset,testset

算法评估函数

def accuray(predict_reselts, method='all'):# 计算均方根误差def rmse(predict_reselts):length = 0_rmse_sum = 0for uid,mid, real_rating, pred_rating in predict_reselts.itertuples(index=False):length+=1_rmse_sum += (pred_rating - real_rating)**2return round(np.sqrt(_rmse_sum/length),4)# 计算绝对值误差def mae(predict_reselts):length=0_mae_sum=0for uid,mid,real_rating,pred_rating in predict_reselts.itertuples(index=False):length +=1_mae_sum += abs(pred_rating-real_rating)return round(_mae_sum/length,4)# 两个都计算def rmse_mae(predict_reselts):length = 0_rmse_sum=0_mae_sum=0for uid,mid,real_rating,pred_rating in predict_reselts.itertuples(index=False):length +=1_mae_sum += abs(pred_rating-real_rating)_rmse_sum += (pred_rating - real_rating)**2return round(np.sqrt(_rmse_sum/length),4),round(_mae_sum/length,4)# 根据输入的参数放回对应的评估方法if method.lower() =='rmse':return rmse(predict_reselts)elif method.lower() == 'mae':return mae(predict_reselts)else:return rmse_mae(predict_reselts)

将数据代入算法和评估函数中

trainset, testset = data_split('ml-latest-small/ratings.csv',random=True)
bcf = BaselineCFBySGD(20,0.1,0.1,['userId','movieId','rating'])
bcf.fit(trainset)
pred_test = bcf.test(testset)
# 生成器对象用list进行转化,然后转化为dataframe格式
df_pred = pd.DataFrame(list(pred_test), columns=[['userId','movieId','rating','pred_rating']])rmse, mae = accuray(df_pred,'all')
print('rmse:',rmse,';mae:',mae)

rmse: 0.8647 ;mae: 0.6595

2.2 Baseline交替最小二乘法算法

step1:交替最小二乘法推导
核心思想:对损失函数求偏导,然后让偏导为0。
损失函数如下:
J ( θ ) = f ( b u , b i ) J(θ) = f(b_u,b_i) J(θ)=f(bu,bi)
对参数求偏导:
∂ ∂ b u J ( θ ) = ∂ ∂ b u f ( b u , b i ) = − 2 ∑ u , i ∈ R ( r u i − u − b u − b i ) + 2 λ b u \frac{∂}{∂b_u}J(\theta) = \frac{∂}{∂b_u}f(b_u,b_i) = -2\sum_{u,i∈R}(r_{ui}-u-b_u-b_i) + 2\lambda b_u buJ(θ)=buf(bu,bi)=2u,iR(ruiububi)+2λbu
偏导为0,则可得:
∑ u , i ∈ R ( r u i − u − b u − b i ) = 2 λ b u \sum_{u,i∈R}(r_{ui}-u-b_u-b_i) = 2\lambda b_u u,iR(ruiububi)=2λbu
∑ u , i ∈ R ( r u i − u − b i ) = ∑ u ∈ R b u + λ b u \sum_{u,i∈R}(r_{ui}-u-b_i) = \sum_{u∈R}b_u+\lambda b_u u,iR(ruiubi)=uRbu+λbu
为了方便计算,令 ∑ u ∈ R b u ≈ ∣ R ( u ) ∣ ∗ b u \sum_{u∈R}b_u≈|R(u)|*b_u uRbuR(u)bu,则可得:
b u : = ∑ u , i ∈ R ( r u i − u − b i ) λ 1 + ∣ R ( u ) ∣ b_u:=\frac{\sum_{u,i∈R}(r_{ui}-u-b_i)}{\lambda_1+|R(u)|} bu:=λ1+R(u)u,iR(ruiubi)

∣ R ( u ) ∣ |R(u)| R(u)表示用户u有评分的数量

同理可得:
b i : = ∑ u , i ∈ R ( r u i − u − b u ) λ 2 + ∣ R ( i ) ∣ b_i:=\frac{\sum_{u,i∈R}(r_{ui}-u-b_u)}{\lambda_2+|R(i)|} bi:=λ2+R(i)u,iR(ruiubu)
step2:交替最小二乘法(ALS)
我们推导了各自的表达式,但表达式互相包含对方,因此我们用交替最小二乘法进行计算:

  1. 先固定其中一项值,求另一个值;
  2. 然后固定另一项值,求第一项的值;如此反复更新二者的值,最后求得结果

要求 b u b_u bu时,先将 b i b_i bi看做已知;求 b i b_i bi时,先将 b u b_u bu看做已知

step3:算法实现
总体代码跟随机梯度下降差不多

# 最小二乘法算法实现
class BaselineCFByALS(object):'''max_epochs 梯度下降迭代次数alpha 学习率reg 过拟合参数columns 数据字段名称'''def __init__(self,max_epochs,reg_bu,reg_bi,columns=['userId','movieId','rating']):self.max_epochs = max_epochsself.reg_bu = reg_buself.reg_bi = reg_biself.columns = columnsdef fit(self,data):''':param data:uid,mid,rating:return:'''self.data = data# 用户评分数据self.users_rating = data.groupby(self.columns[0]).agg([list])[[self.columns[1], self.columns[2]]]# 电影评分数据self.items_rating = data.groupby(self.columns[1]).agg([list])[[self.columns[0], self.columns[2]]]# 全局平均分self.global_mean = self.data[self.columns[2]].mean()# 调用随机梯度下降训练模型参数self.bu,self.bi = self.als()def als(self):'''最小二乘法,优化bu和bi值:return: bu bi'''bu = dict(zip(users_rating.index, np.zeros(len(users_rating))))bi = dict(zip(items_rating.index, np.zeros(len(items_rating))))for i in range(max_epochs):# 计算bifor mid, uids, real_ratings in items_rating.itertuples(index=True):_sum=0for uid,rating in zip(uids,real_ratings):_sum += rating - global_mean-bu[uid]bi[mid] = _sum/(self.reg_bi+len(uids))# 计算bufor uid,mids,real_ratings in users_rating.itertuples(index=True):_sum=0for mid,rating in zip(mids,real_ratings):_sum+= rating -self.global_mean-bi[mid]bu[uid] = _sum/(self.reg_bu+len(mids))return bu,bidef predict(self,uid,mid):'''使用评分公式进行预测param uid,mid;return predict_rating;'''predict_rating = self.global_mean+self.bu[uid]+self.bi[mid]return predict_ratingdef test(self,testset):'''使用预测函数预测测试集数据param testset;return yield;'''for uid,mid,real_rating in testset.itertuples(index=False):try:# 使用predict函数进行预测pred_rating = self.predict(uid,mid)except Exception as e:print(e)else:# 返回生成器对象yield uid,mid,real_rating,pred_rating

应用最小二乘法算法

trainset, testset = data_split('ml-latest-small/ratings.csv',random=True)
bcf = BaselineCFByALS(20,25,15,['userId','movieId','rating'])
bcf.fit(trainset)
pred_test = bcf.test(testset)
# 生成器对象用list进行转化,然后转化为dataframe格式
df_pred_als = pd.DataFrame(list(pred_test), columns=[['userId','movieId','rating','pred_rating']])
rmse, mae = accuray(df_pred_als,'all')
print('rmse:',rmse,';mae:',mae)

rmse: 0.8403 ;mae: 0.6462

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/31784.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

华纳云:Ubuntu安装Drupal报错怎么解决

在安装 Drupal 过程中遇到错误可能是由于各种原因引起的。以下是一些常见的安装 Drupal 时可能遇到的问题以及相应的解决方法: 数据库连接问题: 安装 Drupal 时需要连接数据库,如果数据库连接配置不正确,可能会导致安装失败。确保…

如何将jar包部署到宝塔

尝试多种方式上传,但启动一直失败,这种方式亲测是好使的 项目内修改位置 在pom.xml文件中将mysql的scope改成provided,如果是固定的版本号会出现问题 之后就可以打包啦,直接点击maven中的package 找到打包文件的位置&#xff…

Object.values()

Object.values() 是ES2017新增的一个对象方法,它可以将一个对象自身的所有可枚举属性值,组成一个数组返回。 基本语法: Object.values(obj)示例: jsCopy codeconst obj {foo: bar,baz: 42 };Object.values(obj); // [bar, 42]Object.values()的特点: 只返回可枚举的属性值…

免费插件-illustrator-Ai插件-印刷功能-二维码生成

文章目录 1.介绍2.安装3.通过窗口>扩展>知了插件4.功能解释5.示例5.1.QR常用二维码5.2.PDF4175.3.EAN13 6.总结 1.介绍 本文介绍一款免费插件,加强illustrator使用人员工作效率,进行二维码生成。首先从下载网址下载这款插件 https://download.csd…

MySQL之深入InnoDB存储引擎——redo日志

文章目录 一、为什么需要redo日志二、redo日志的类型1)简单的redo日志类型2)复杂的redo日志类型 三、Mini-Transaction四、redo日志的写入过程五、redo日志文件1、刷盘时机2、redo日志文件组 六、log sequence number1、lsn的引入2、flushed_to_disk_lsn…

npm ERR! cb.apply is not a function

当NPM版本过低导致 npm ERR! cb.apply is not a function 1. win r 打开运行,输入%appdata% 2. 删除 npm 和 npm-cache 文件夹 3. 执行npm cache clean --force命令 如果还不行,就执行卸载Node.js重新安装。

java 文件/文件夹复制,添加压缩zip

复制文件夹,并压缩成zip 需求:创建A文件夹,把B文件夹复制到A文件夹。然后把A文件夹压缩成zip包 public static void main(String[] args) throws Exception {try {String A "D:\\dev\\program";String B "D:\\program";// 创建临…

Vue 插槽 slot

solt 插槽需要分为 2.6.0 版本以上和 2.6.0版本以下。 2.6.0 版本以下的 slot 插槽在,2.x版本将继续支持,但是在 Vue 3 中已被废弃,且不会出现在官方文档中。 作用 插槽 prop 允许我们将插槽转换为可复用的模板,这些模板可以基于…

Qt应用开发(基础篇)——LCD数值类 QLCDNumber

一、前言 QLCDNumber类继承于QFrame,QFrame继承于QWidget,是Qt的一个基础小部件。 框架类QFrame介绍 QLCDNumber用来显示一个带有类似lcd数字的数字,适用于信号灯、跑步机、体温计、时钟、电表、水表、血压计等仪器类产品的数值显示。 QLCDNu…

【CSS】文本效果

文本溢出、整字换行、换行规则以及书写模式 代码&#xff1a; <style> p.test1 {white-space: nowrap; width: 200px; border: 1px solid #000000;overflow: hidden;text-overflow: clip; }p.test2 {white-space: nowrap; width: 200px; border: 1px solid #000000;ove…

2023年Q2天猫洗衣机行业品牌销售排行榜(淘宝天猫数据)

洗衣机作为普及率极高的家电之一&#xff0c;如今已经成为我们生活中不可或缺的一部分。由于洗衣机的普及率较高&#xff0c;因此虽其市场规模庞大&#xff0c;但如今要使洗衣机呈现规模化增长的可能性还是比较小的。不过&#xff0c;随着用户需求及产品的升级&#xff0c;洗衣…

【C++中的pair类型】用于将两个不同类型的元素组合成一个单元,即key-value

文章目录 1、定义和初始化2、使用方式 1、定义和初始化 pair<type1, type2> p; // 初始化方法有3种 pair<int, string> p make_pair(1, "one"); pair<int, string> p {1, "one"}; pair<int, string> p(1, "one");2、…

【docker】 运行bytetrack 构建映像失败 使用docker删除之前构建的映像

1 Docker删除docker build失败的images docker images | grep "<none>" | awk {print $3} | xargs docker rmi 2 Docker删除启动失败的image docker ps -a | awk {if (length($2) 12){print $1}} | xargs docker rm

Apipost接口测试断言

常用断言直接点右边栏 断言list&#xff1a; // 断言json数组长度 apt.assert(response.json.data.data.length20); // 断言json数组中的某个对象 apt.assert(response.json.data.data[0].docid1482);

EvilBox One靶场笔记

EvilBox: One靶场笔记 信息收集 先fscan找主机192.168.1.102 namp扫端口 开放80,22端口 然后扫目录 └─$ gobuster dir -r -u http://192.168.1.102/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,txt,bak,html在扫secret目录&#xff0c;找…

c++ 子数组动态规划问题

1.最大子数组和 力扣 给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#xff0c;返回其最大和。 子数组 是数组中的一个连续部分。 示例 1&#xff1a; 输入&#xff1a;nums [-2,1,-3,4,-1,2,1,-5…

基于Kubeadm部署k8s集群:下篇

继续上篇内容 目录 7、安装flannel 8、节点管理命令 三、安装Dashboard UI 1、部署Dashboard 2、开放端口设置 3、权限配置 7、安装flannel Master 节点NotReady 的原因就是因为没有使用任何的网络插件&#xff0c;此时Node 和Master的连接还不正常。目前最流行的Kuber…

微服务04-elasticsearch

1、es概念 1.1 文档和字段 elasticsearch是面向**文档(Document)**存储的,可以是数据库中的一条商品数据,一个订单信息。文档数据会被序列化为json格式后存储在elasticsearch中: 而Json文档中往往包含很多的字段(Field),类似于数据库中的列。 1.2 索引和映射 索引(…

Visual Studio 2019 详细安装教程(图文版)

前言 Visual Studio 2019 安装包的下载教程、安装教程 教程 博主博客链接&#xff1a;https://blog.csdn.net/m0_74014525 关注博主&#xff0c;后期持续更新系列文章 ********文章附有百度网盘安装包链接********* 系列文章 第一篇&#xff1a;Visual Studio 2019 详细安装教…

Spark(39):Streaming DataFrame 和 Streaming DataSet 输出

目录 0. 相关文章链接 1. 输出的选项 2. 输出模式(output mode) 2.1. Append 模式(默认) 2.2. Complete 模式 2.3. Update 模式 2.4. 输出模式总结 3. 输出接收器(output sink) 3.1. file sink 3.2. kafka sink 3.2.1. 以 Streaming 方式输出数据 3.2.2. 以 batch …