【电影推荐系统】基于 ALS 的协同过滤推荐算法

目录

目的

用户电影推荐矩阵主要思路如下

1 UserId 和 MovieID 做笛卡尔积,产生(uid,mid)的元组

2 通过模型预测(uid,mid)的元组。

3 将预测结果通过预测分值进行排序。

4 返回分值最大的 K 个电影,作为当前用户的推荐。

5 排序根据余弦相似度进行排序;

6 用均方根误差RMSE进行模型评估和参数调整   ;

由于电影的特征是稳定不变的,所以离线训练电影相似矩阵,节约实时计算时间。

 完整代码

offlineRecommender.scala

ALSTrainer.scala


目的

训练出用户电影推荐矩阵,电影相似度矩阵。

用户电影推荐矩阵主要思路如下

1 UserId 和 MovieID 做笛卡尔积,产生(uid,mid)的元组

val userMovies = userRDD.cartesian(movieRDD)

2 通过模型预测(uid,mid)的元组。

3 将预测结果通过预测分值进行排序。

4 返回分值最大的 K 个电影,作为当前用户的推荐。

核心代码    

val preRatings = model.predict(userMovies)
    val userRecs = preRatings
      .filter(_.rating > 0)
      .map(rating => (rating.user, (rating.product, rating.rating)))
      .groupByKey()
      .map {
        case (uid, recs) => UserResc(uid, recs.toList
          .sortWith(_._2 > _._2)
          .take(USER_MAX_RECOMMENDATION)
          .map(
            x => Recommendation(x._1, x._2)
          )
        )
      }
      .toDF()

5 排序根据余弦相似度进行排序;

6 用均方根误差RMSE进行模型评估和参数调整   ;

均方根误差RMSE公式

核心代码 

 def adjustALSParam(trainRDD: RDD[Rating], testRDD: RDD[Rating]): Unit = {
    val result = for (rank <- Array(50, 100, 200, 300); lambda <- Array(0.01, 0.1, 1 ))
      yield {
        val model = ALS.train(trainRDD, rank, 5, lambda)
        val rmse = getRMSE(model, testRDD)
        (rank, lambda, rmse)
      }
    println(result.minBy(_._3)

由于电影的特征是稳定不变的,所以离线训练电影相似矩阵,节约实时计算时间。

核心代码   

val movieFeatures = model.productFeatures.map{
        case (mid, feature) => (mid, new DoubleMatrix(feature))
      }
     val movieRecs = movieFeatures.cartesian(movieFeatures)
       .filter{
         case (a,b ) => a._1 != b._1
       }
       .map{
         case(a, b) => {
           val simScore = this.consinSim(a._2, b._2)
           ( a._1, (b._1, simScore) )
         }
       }
       .filter(_._2._2 > 0.6)   
       .groupByKey()
       .map{
         case (mid, item) => MoiveResc(mid, item
           .toList
           .sortWith(_._2 > _._2)
           .map(x => Recommendation(x._1, x._2)))
       }
       .toDF()

 完整代码

offlineRecommender.scala

package com.qh.offline

import org.apache.spark.SparkConf
import org.apache.spark.mllib.recommendation.{ALS, Rating}
import org.apache.spark.sql.SparkSession
import org.jblas.DoubleMatrix

//跟sparkMl 里面区分
case class MovieRating(uid: Int, mid: Int, score: Double, timestamp: Int)

case class MongoConfig(uri: String, db: String)

//基准推荐对象
case class Recommendation(mid: Int, score: Double)

//基于预测评分的用户推荐列表
case class UserResc(uid: Int, recs: Seq[Recommendation]) //类别, top10基准推荐对象

//基于LFM电影特征向量的电影相似度列表
case class MoiveResc(mid: Int, recs: Seq[Recommendation])

/**
 * 基于隐语义模型的协同过滤 推荐
 *
 * 用户电影推荐矩阵
 * 通过ALS训练出来的Model计算所有当前用户电影的推荐矩阵
 * 1. UserID和MovieId做笛卡尔积
 * 2. 通过模型预测uid,mid的元组
 * 3. 将预测结果通过预测分值进行排序
 * 4. 返回分值最大的K个电影,作为推荐
 * 生成的数据结构 存到UserRecs表中
 *
 * 电影相似度矩阵
 * 模型评估和参数选取
 *
 */
object offline {

  val MONGODB_RATING_COLLECTION = "Rating"
  val USER_RECS = "UserRecs"
  val MOVIE_RECS = "MovieRecs"
  val USER_MAX_RECOMMENDATION = 20

  def main(args: Array[String]): Unit = {
    val config = Map(
      "spark.cores" -> "local[*]",
      "mongo.uri" -> "mongodb://hadoop100:27017/recommender",
      "mongo.db" -> "recommender"
    )
    val sparkConf = new SparkConf().setMaster(config("spark.cores")).setAppName("offline")
    val spark = SparkSession.builder().config(sparkConf).getOrCreate()
    import spark.implicits._
    implicit val mongoConfig = MongoConfig(config("mongo.uri"), config("mongo.db"))

    //加载数据
    val ratingRDD = spark.read
      .option("uri", mongoConfig.uri)
      .option("collection", MONGODB_RATING_COLLECTION)
      .format("com.mongodb.spark.sql")
      .load()
      .as[MovieRating]
      .rdd
      .map(rating => (rating.uid, rating.mid, rating.score)) //去掉时间戳
      .cache() //缓存,诗酒花在内存中
    //预处理
    val userRDD = ratingRDD.map(_._1).distinct()
    val movieRDD = ratingRDD.map(_._2).distinct()

    //训练LFM
    val trainData = ratingRDD.map(x => Rating(x._1, x._2, x._3))
    val (rank, iterations, lambda) = (200, 5, 0.1)
    val model = ALS.train(trainData, rank, iterations, lambda)

    //基于用户和电影的隐特征,计算预测评分,得到用户的推荐列表
    //    笛卡尔积空矩阵
    val userMovies = userRDD.cartesian(movieRDD)

    //预测
    val preRatings = model.predict(userMovies)

    val userRecs = preRatings
      .filter(_.rating > 0)
      .map(rating => (rating.user, (rating.product, rating.rating)))
      .groupByKey()
      .map {
        case (uid, recs) => UserResc(uid, recs.toList
          .sortWith(_._2 > _._2)
          .take(USER_MAX_RECOMMENDATION)
          .map(
            x => Recommendation(x._1, x._2)
          )
        )
      }
      .toDF()

    userRecs.write
      .option("uri", mongoConfig.uri)
      .option("collection", USER_RECS)
      .mode("overwrite")
      .format("com.mongodb.spark.sql")
      .save()

    //基于电影隐特征,计算相似度矩阵,得到电影的相似度列表
    val movieFeatures = model.productFeatures.map{
        case (mid, feature) => (mid, new DoubleMatrix(feature))
      }

    //电影与电影 笛卡尔积
     val movieRecs = movieFeatures.cartesian(movieFeatures)
       .filter{
         case (a,b ) => a._1 != b._1
       }
       .map{
         case(a, b) => {
           val simScore = this.consinSim(a._2, b._2)
           ( a._1, (b._1, simScore) )
         }
       }
       .filter(_._2._2 > 0.6)    //过滤出相似度
       .groupByKey()
       .map{
         case (mid, item) => MoiveResc(mid, item
           .toList
           .sortWith(_._2 > _._2)
           .map(x => Recommendation(x._1, x._2)))
       }
       .toDF()

    movieRecs.write
      .option("uri", mongoConfig.uri)
      .option("collection", MOVIE_RECS)
      .mode("overwrite")
      .format("com.mongodb.spark.sql")
      .save()

    spark.stop()
  }

  /*
  求解余弦相似度
   */
  def consinSim(matrixA: DoubleMatrix, matrixB: DoubleMatrix):Double = {
//    .dot 点乘  .norm2 L2范数,就是模长
    matrixA.dot(matrixB) / (matrixA.norm2() * matrixB.norm2())
  }
}

ALSTrainer.scala

package com.qh.offline

import breeze.numerics.sqrt
import com.qh.offline.offline.MONGODB_RATING_COLLECTION
import org.apache.spark.mllib.recommendation.{ALS, MatrixFactorizationModel, Rating}
import org.apache.spark.SparkConf
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.SparkSession

/**
 * 调参
 * 通过ALS寻找lMF参数,打印到控制台输出
 */
object ALSTrainer {
  def main(args: Array[String]): Unit = {
    val config = Map(
      "spark.cores" -> "local[*]",
      "mongo.uri" -> "mongodb://hadoop100:27017/recommender",
      "mongo.db" -> "recommender"
    )
    val sparkConf = new SparkConf().setMaster(config("spark.cores")).setAppName("offline")
    val spark = SparkSession.builder().config(sparkConf).getOrCreate()
    import spark.implicits._
    implicit val mongoConfig = MongoConfig(config("mongo.uri"), config("mongo.db"))
    val ratingRDD = spark.read
      .option("uri", mongoConfig.uri)
      .option("collection", MONGODB_RATING_COLLECTION)
      .format("com.mongodb.spark.sql")
      .load()
      .as[MovieRating]
      .rdd
      .map(rating => Rating(rating.uid, rating.mid, rating.score)) //去掉时间戳
      .cache()
    //   随机切分数据集=>训练集 测试集
    val splits = ratingRDD.randomSplit(Array(0.8, 0.2))
    val trainRDD = splits(0)
    val testRDD = splits(1)

    //    模型参数选择
    adjustALSParam(trainRDD, testRDD)

    spark.close()

  }

  def adjustALSParam(trainRDD: RDD[Rating], testRDD: RDD[Rating]): Unit = {
    val result = for (rank <- Array(50, 100, 200, 300); lambda <- Array(0.01, 0.1, 1 ))
      yield {
        val model = ALS.train(trainRDD, rank, 5, lambda)
        val rmse = getRMSE(model, testRDD)
        (rank, lambda, rmse)
      }
    println(result.minBy(_._3))
  }

  def getRMSE(model: MatrixFactorizationModel, data: RDD[Rating]): Double = {
    //      计算预测评分
    val userProducts = data.map(item => (item.user, item.product))
    val predictRating = model.predict(userProducts)
    //uid 和 mid 交集 做被减数
    val observed = data.map(item => ((item.user, item.product), item.rating)) //实际观测值和预测值
    val predict = predictRating.map(item => ((item.user, item.product), item.rating))

    sqrt(
      observed.join(predict).map {
        case ((uid, mid), (actual, pre)) => //内连接没有数据冗余,不需要groupby
          val err = actual - pre
          err * err
      }.mean()
    )
  }

}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
【项目介绍】 基于Django协同过滤和spark-als电影推荐系统源码+项目使用说明+设计报告.zip 该资源内项目代码都是经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载使用,也适合小白学习进阶, 或者实际项目借鉴参考! 当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。如果基础还行,也可在此代码基础上进行修改,以实现其他功能。 说明 1. 新手建议结合pycharm使用,https://www.jetbrains.com/pycharm/,下载专业版试用30天。 2. 注册普通用户通过web界面来设置,创建创建用户通过creeatsuperuser创建。下文有详细命令 3. 导入电影信息通过insert_movies_script.py来操作 (会删除已有的所有信息!) 4. 前端展示 浏览最多,评分最多,收藏最多,写的比较直白,你可以改的委婉点: 最热电影,火爆排行...之类的。每种有10条。 我猜你喜欢为基于用户推荐,item推荐为基于项目推荐。两种推荐思路下文有介绍 系统采用的技术 前端: bootstrap3 css 框架 后端: django 2.2.1 + sqlite3数据库 (MVC框架) 数据: python异步爬虫从豆瓣top250抓取数据,保存到本地csv文件中 主要功能: 录入图书信息,用户打分,电影标签分类,电影推荐电影分享,电影收藏,后台管理系统。 整体采用MVC架构,前端页面通过django template模板来实现,实现了模板的复用功能。同时前端页面的组织结构较为清晰。 推荐算法思路 通过协调过滤计算和其他用户的距离,然后进行筛选。如果用户数量不足,推荐数目不够15条,就会自动从 所有未打分的电影中按照浏览数降序选一部分填充进去。 基于用户的推荐 1. 用户需要给电影打分。通过用户已打分的部分来计算相似度,如果用户未打分,或者没有其他用户,则按照浏览数降序返回。 2. 通过pearson算法来计算用户之间的距离,找到距离最近的N个用户。将这些用户中已打分的电影(且要推荐的用户未看过的部分)返回。 基于item的推荐 1. 计算物品相似度矩阵: https://www.jianshu.com/p/27b1c035b693 2. 遍历当前用户已打分的item,计算和未打分的item的相似距离。 3. 对相似距离进行排序 返回 主要实现的功能 1. 登录注册页面 2. 基于协同过滤电影的分类,排序,搜索,打分,排序功能。 3. 基于协同过滤的周推荐和月推荐 4. 观影分享会等活动功能,用户报名功能 (需要额外添加) 5. 发帖留言论坛功能 (要额外添加) 6. 基于spark的ALS算法 (要额外添加) 7. Mysql适配 8. movielens数据集适配 fixed 1. 首页导航栏链接错误 2. 首页面为空 3. 登录注册页面 4. 推荐跳转登录 5. 周推荐用户没有评分时随机推荐 6. 按照收藏数量排序 7. 重新设计了 action 和UserAction model,拆分出了UserAction 电影模型 1. 浏览量 每次刷新页面的浏览数 2. 收藏量 user manytomany field 每个用户收藏一次 3. 评分 rate 每个用户评分一次 4. 在电影下面的评论加点赞功能 安装运行方法 安装依赖 1. 将项目导入pycharm, 在pycharm配置python解释器,3.7及以下都可以。可以通过conda或者其他的虚拟环境来安装 2. 打开终端 输入pip install -r requirements.txt 若提示无pip。去下载get-pip.py 运行python get-pip.py 3. 在pip安装过程中如果报错C++ 14依赖问题。则安装c++依赖工具。找不到找我要。如果安装速度过慢,请更换国内镜像https://blog.csdn.net/chenghuikai/article/details/55258957 4. 安装成功后,进入运行阶段 运行 1. 运行服务器: python manage.py runserver 2. 如果无数据,运行项目根目录下的数据迁移脚本 populate开头。 3. python manage.py createsuperuser 创建超级管理员, (密码输入时终端暂时看不到) 4. 进入后台: 127.0.0.1:8000/admin
电影推荐系统是基于用户历史行为和兴趣特征,为用户提供个性化的电影推荐服务。本文将介绍基于Spark的电影推荐系统设计与实现。 1. 数据获取与预处理 首先,我们需要获取电影数据集。可以从MovieLens等网站下载公开的电影评分数据集。数据集包括用户对电影的评分、电影信息等。对于电影信息,我们需要将其转换为向量表示,以便后续计算。可以使用word2vec等算法电影信息转换为向量。 2. 特征工程 在进行推荐之前,需要对数据进行特征工程。电影推荐系统通常使用协同过滤算法,该算法需要构建用户-电影评分矩阵。我们可以使用Spark的MLlib库中的ALS算法来构建矩阵,并进行模型训练。 3. 模型训练与优化 使用ALS算法构建用户-电影评分矩阵后,我们需要对模型进行训练和优化。可以使用Spark的MLlib库中的交叉验证等技术来优化模型参数,提高模型的推荐效果。 4. 推荐服务 完成模型训练后,我们可以使用Spark Streaming构建推荐服务,为用户提供个性化的电影推荐服务。推荐服务需要实现用户的登录、电影推荐推荐结果展示等功能。 5. 性能优化 在实际应用中,电影推荐系统需要处理海量的数据。为了提高推荐效率,我们可以使用Spark的分布式计算能力,将计算任务分布到多个节点上并行处理。此外,我们还可以使用Spark的缓存技术,将常用数据缓存到内存中,提高计算效率。 总之,基于Spark的电影推荐系统设计与实现需要进行数据获取与预处理、特征工程、模型训练与优化、推荐服务和性能优化等步骤。通过以上步骤,我们可以构建出高效、准确的电影推荐系统,为用户提供更好的推荐服务。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

返返返

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

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

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

打赏作者

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

抵扣说明:

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

余额充值