PySpark推荐模型简明教程

一、PySpark推荐模型

  PySpark的ALS(Alternating Least Squares,交替最小二乘法)推荐模型是一种基于矩阵分解的协同过滤算法,广泛应用于推荐系统中。ALS算法通过分解用户-物品交互矩阵来发现用户和物品的潜在特征(或称为因子),进而预测用户对未交互物品的评分或喜好,从而生成推荐。
  PySpark的ALS模型提供了多种推荐生成方法,如recommendForAllUsers(为所有用户生成推荐)、recommendForUserSubset(为指定用户子集生成推荐)、recommendForAllItems(为所有物品生成推荐用户)等。

二、推荐模型ALS应用案例1:电影推荐(单表)

  数据集有3个字段,分别为:用户ID,电影ID,评分。数据集中一共有6个用户,7部电影。其中:用户ID=1的用户,观看了3部电影,并且还给电影评分,有4部电影没有观看。现在的问题是,剩下的4部电影,如何推荐给“1”号用户?哪部电影优先推荐?
在这里插入图片描述

1.加载数据

  配置Spark程序,并加载数据集:电影评分.csv,该数据集存放在HDFS系统的/data/目录下。最后将数据集划分为训练集(占70%)和测试集(占30%)。

from pyspark import SparkConf
from pyspark.ml.feature import StringIndexer
from pyspark.sql import SparkSession
from pyspark.sql.functions import *
from pyspark.ml.recommendation import ALS
from pyspark.ml.evaluation import RegressionEvaluator

conf = SparkConf().setAppName("简单电影推荐(单表)").setMaster('spark://192.168.126.10:7077')
sc = SparkContext.getOrCreate(conf)
spark = SparkSession(sc)

# ------------1.加载数据--------------------
filename = "电影评分.csv"
df_movies = spark.read.csv('hdfs://192.168.126.10:9000/data/' + filename, header=True, inferSchema=True)
df_movies.show()
train, test = df_movies.randomSplit([0.7, 0.3], seed=8)   # 划分训练集,测试集

  加载的数据集部分数据如下:
在这里插入图片描述

2.创建、评估推荐模型

(1)创建推荐模型ALS(基于交替最小二乘法求解协同过滤模型),ALS的参数为:

  • maxIter:最大迭代次数。
  • userCol:用户ID的数据字段。
  • itemCol:物品ID的数据字段。
  • ratingCol:评分的数据字段。
  • nonnegative:是否对ALS使用非负的限制,True不创建负数评分。
  • coldStartStrategy:在测试阶段用于处理未知或新用户/物品的策略,drop不对非数字评分进行预测。

  推荐模型ALS的最终任务:根据userCol和itemCol,预测 ratingCol。
(2)用训练集拟合(训练)推荐模型rec。
(3)用训练好的推荐模型rec_model ,预测测试集test的“评分”,得到结果predicted_ratings。
在这里插入图片描述

(4)创建评估器。因为预测的电影评分是连续值,因此用回归模型评估器RegressionEvaluator,评估指标为均方根误差rmse,最后得到推荐模型ALS的均方根误差如下。
在这里插入图片描述
  代码如下:

# -----------3.构建、评估智能推荐模型------------------
print("---------3.1构建智能推荐模型-------")
rec = ALS(maxIter=10, userCol='用户ID',
          itemCol='电影ID', ratingCol='评分',
          nonnegative=True, coldStartStrategy="drop")  # 构建推荐模型ALS(基于交替最小二乘法求解协同过滤模型)
rec_model = rec.fit(train)   # 用训练数据集拟合(训练)模型

predicted_ratings = rec_model.transform(test)    # 用训练好的模型rec_model预测 测试集test
print("\n预测评分(prediction):")
#predicted_ratings.orderBy(rand()).show(10)     # 随机显示预测结果
predicted_ratings.show()

print(" -------------3.2 评估智能推荐模型-------------")
evaluator = RegressionEvaluator(metricName='rmse',
                                predictionCol='prediction',
                                labelCol='评分')   # 电影评分数据是连续值,因此用回归模型评估器。评估指标是均方根误差rmse。
rmse = evaluator.evaluate(predicted_ratings)
print("推荐模型的均方根误差rmse(越小越好)为:", rmse)

3. 向用户推荐电影

  现在计划向用户ID为“1”的用户推荐电影,编程思路如下。
(1)统计电影数量及电影ID号

在这里插入图片描述
  有7部电影,电影ID号为:101-107。

print("\n-------------4.1 统计电影数量及电影ID----")
all_movies = df_movies.select('电影ID').distinct()   # 对电影ID去重,确定有多少部电影
print("电影数量:", all_movies.count())
all_movies.show(10)

(2)统计用户1 已经观看的电影ID
在这里插入图片描述
  1号用户已经观看了电影:101,102,103。

print("\n----------4.2 统计用户1 已经观看的电影ID----")
userId = 1
watched_movies = df_movies \
    .filter(df_movies['用户ID'] == userId) \
    .select('电影ID') \
    .distinct()
watched_movies.show(10)

(3)统计用户1 未观看的电影ID
  统计用户1未观看的电影ID,并增加一列数据:用户ID,值全部为1号用户的ID号:1。因为后面的推荐模型预测评分,需要两个字段:电影ID和用户ID。
在这里插入图片描述

  1号用户没有观看的电影为:104,105,106,107。

print("\n--------4.3 统计用户1 未观看的电影ID----")
remaining_movies = all_movies.exceptAll(watched_movies).withColumn('用户ID', lit(int(userId)))
print('未观影数据:')
remaining_movies.show(10)

(4)预测用户1对未观看电影的评分
  使用前面训练好的推荐模型rec_model,预测用户1对观看电影remaining_movies的评分,并按评分降序排序。

在这里插入图片描述

print("---------4.4 由高到低 向用户推荐电影---------------")
# 根据userCol和itemCol,预测 ratingCol。即根据用户ID和电影ID,预测电影评分
recommendations = rec_model.transform(remaining_movies)   # 用训练好的模型,使用未观看的电影数据,预测用户对电影的评分,自动新增一列prediction。
print("预测用户1 对电影的评分:")
recommendations.orderBy('prediction', ascending=False).show()

(5)限制预测评分不超过5
  计算预测评分的最大值(max_rating),并将每个预测评分除以这个最大值后乘以5,以此来将评分标准化到一个不超过5的范围内。
在这里插入图片描述

# 将预测评分除以最高预测评分,再乘以5,使预测评分不超过5分。
max_rating = recommendations.agg(max('prediction')).collect()[0][0]
print("预测的最高评分:", max_rating)

final_recommendations = recommendations.withColumn("scaled_prediction", (col("prediction")/max_rating)*5)
final_recommendations.orderBy('scaled_prediction', ascending=False).show(5)

  因此,按照预测评分排序,向1号用户推荐的电影依次是:104,107,105,106。

三、推荐模型ALS应用案例2:电影推荐(联表)

1.数据集介绍

  本案例包含两个数据集:评分表ratings.csv和电影表movies.csv。数据集ratings有100836条记录,部分数据如下:
在这里插入图片描述
  数据集ratings的字段含义如下:

  • userId:用户ID号。
  • movieId:电影ID号。
  • rating:用户对电影的评分。
  • timestamp:时间戳,评分时间。

  数据集movies有9742条记录,部分数据如下:
在这里插入图片描述
  数据集moveis的字段含义如下:

  • movieId:电影ID号。
  • title:电影名称。
  • genres:电影类型,如动作片,喜剧片 ,恐怖片等。

2.加载数据集

  数据集ratings和movies存放在HDFS的/data/目录下,加载数据集,并连接合并两个数据集。因为推荐电影时,只需要用户ID,电影ID,电影名称和电影评分,因此选择字段:‘userId’, ‘movieId’, ‘title’, ‘rating’。连接合并后的数据集部分数据如下。
在这里插入图片描述
  代码如下:

from pyspark import SparkConf
from pyspark.ml.feature import StringIndexer
from pyspark.sql import SparkSession
from pyspark.sql.functions import *
from pyspark.ml.recommendation import ALS
from pyspark.ml.evaluation import RegressionEvaluator

conf = SparkConf().setAppName("电影推荐(联表)").setMaster('spark://192.168.126.10:7077')
sc = SparkContext.getOrCreate(conf)
spark = SparkSession(sc)
# ------------1.加载数据--------------------
df_movies = spark.read.csv('hdfs://192.168.126.10:9000/data/movies.csv', header=True, inferSchema=True)
df_ratings = spark.read.csv('hdfs://192.168.126.10:9000/data/ratings.csv', header=True, inferSchema=True)

print(df_movies.count(), len(df_movies.columns))
print(df_ratings.count(), len(df_ratings.columns))
df_movies.show(5)
df_ratings.show(5)
print("------------表连接-------------")
df_total_movies = df_movies \
    .join(df_ratings, 'movieId', how='left') \
    .select('userId', 'movieId', 'title', 'rating')   # 推荐电影时,只需要用户ID,电影ID,电影名称和电影评分
df_total_movies.show(6, False)

3.数值化字符串列

  数据集中,title列的值是字符串类型,需转为数值型数据。数值化后的数据集部分数据如下。
在这里插入图片描述
  最后将数据集划分为训练集train和测试集test 。

# -----------2.转换和处理数据--------------------
stringIndexer = StringIndexer(inputCol='title', outputCol='title_new')   # 字符串索引器。字符型列title,转换为数值型
model = stringIndexer.fit(df_total_movies)    # 训练转换模型
indexed = model.transform(df_total_movies)    # 转换数据集,原数据集新增一列数值化数据title_new。新数据集名为indexed
indexed.show(5)
train, test = indexed.randomSplit([0.7, 0.3], seed=1)   # 拆分数据集为训练集和测试集
print(f"训练集行数为:{train.count()},测试集行数为:{test.count()}")

train2 = train.dropna(subset=['userId', 'movieId'])  # 删除 训练集的两列数据userId和movieId的缺失值。
test2 = test.dropna(subset=['userId', 'movieId'])    # 删除 测试集的两列数据userId和movieId的缺失值。

4.创建、评估推荐模型

(1)创建推荐模型
  创建ALS推荐模型,并用训练集拟合模型,最后用拟合好的推荐模型预测测试集。预测结果部分数据 如下。
在这里插入图片描述

print("---------3.1构建智能推荐模型-------")
rec = ALS(maxIter=10, userCol='userId',
          itemCol='movieId', ratingCol='rating',
          nonnegative=True, coldStartStrategy="drop")  # 构建推荐模型ALS(基于交替最小二乘法求解协同过滤模型)
rec_model = rec.fit(train2)   # 用训练数据拟合(训练)模型

predicted_ratings = rec_model.transform(test2)  # 用 训练好的模型 预测测试集数据
print("\n预测评分(pediction),rating为实际评分:")
predicted_ratings.orderBy(rand()).show(10)     # 随机显示预测结果

(2)评估模型
  创建回归模型评估器RegressionEvaluator。
在这里插入图片描述

print(" -------------3.2 评估智能推荐模型-------------")
evaluator = RegressionEvaluator(metricName='rmse',
                                predictionCol='prediction',
                                labelCol='rating')   # 电影评分数据是连续数据,因此用回归模型评估器。评估指标是均方根误差rmse。
rmse = evaluator.evaluate(predicted_ratings)
print("推荐模型的均方根误差rmse(越小越好)为:", rmse)

5.向用户推荐电影

  向ID为“45”的用户推荐电影。
(1) 统计电影数量及电影ID
在这里插入图片描述
  电影总数9742部。

# -------------4.向用户推荐电影---------------
print("\n-------------4.1 统计电影数量及电影ID----")
all_movies = df_total_movies.select('movieId').distinct()   # 对电影ID去重,确定有多少部电影
print("电影数量:", all_movies.count())
all_movies.show(10)

(2)统计用户45已经观看的电影ID
在这里插入图片描述

print("\n----------4.2 统计用户45已经观看的电影ID----")
userId = 45
watched_movies = df_total_movies \
    .filter(df_total_movies['userId'] == userId) \
    .select('movieId') \
    .distinct()
watched_movies.show(10)

(3)统计用户45未观看的电影ID
  统计用户45还没观看的电影ID,并增添一列:userId,值全部为45,为后续的推荐模型预测 做准备。
在这里插入图片描述

print("\n--------4.3 统计用户45未观看的电影ID----")
remaining_movies = all_movies.exceptAll(watched_movies).withColumn('userId', lit(int(userId)))
print('未观影数据:')
remaining_movies.show(10)

(4)推荐模型预测用户45对未观看电影的评分
在这里插入图片描述

print("---------4.4 由高到低 向用户推荐电影---------------")
# 根据userCol和itemCol,预测 ratingCol。即根据用户ID和电影ID,预测电影评分
recommendations = rec_model.transform(remaining_movies)   # 用训练好的模型,使用未观看的电影数据,预测用户对电影的评分,新增一列prediction。
recommendations.show(5)

(5)预测数据与原始电影数据集合并
  为查看推荐电影的详情(如电影名称、电影类型等),将预测数据与原始电影数据集合并。
在这里插入图片描述
  按预测评分降序排序。
在这里插入图片描述

final_recommendations = recommendations.join(df_movies, 'movieId', how='left')  # 预测数据和原始电影数据集合并
final_recommendations.show(5)

print("向用户45推荐的电影(从高分到低分排序):")
final_recommendations.select('movieId', 'userId', 'prediction', 'title') \
    .orderBy('prediction', ascending=False)\
    .show(10, False)

(6)限制预测评分不超过5分
  限制预测评分不超过5分。
在这里插入图片描述
  按预测评分降序排序,向用户45推荐。
在这里插入图片描述

# 将预测评分除以最高预测评分,再乘以5,使预测评分不超过5分。
max_rating = final_recommendations.agg(max('prediction')).collect()[0][0]
print(max_rating)
final_recommendations = final_recommendations.withColumn("scaled_prediction", (col("prediction")/max_rating)*5)
final_recommendations.orderBy('prediction', ascending=False).show(5)

print("预测评分缩放后,向用户45推荐的电影(从高分到低分排序):")
final_recommendations.select('movieId', 'userId', 'scaled_prediction', 'title') \
    .orderBy('prediction', ascending=False) \
    .show(10, False)

参考资料:
1.Spark MLlib ----- ALS算法:https://kayleigh.blog.csdn.net/article/details/135457226
2.Pyspark+关联规则 Kaggle购物篮分析案例:https://blog.csdn.net/thorn_r/article/details/138351087
3.Spark 关联规则挖掘:https://blog.csdn.net/weixin_39709476/article/details/109223271
4.大数据–关联规则挖掘案例:https://blog.csdn.net/qq_51641196/article/details/128478588

  • 14
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

侧耳倾听童话

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值