实现movielen电影推荐

本人原创转载请注明出处

下面介绍 MLib 进行个性化的电影推荐应用
通过 Berkely 的这个典型案例,用户可以更加深入地理解 MLlib 以及学会如何构建自己的 MLlib 应用
例中使用 MovieLenss 收集的 72000 名用户在 1 万部影片上的 1 千万个评分数据集
这里 假定这个数据集已经预加载进集群的 HDFS 文件夹 / movielens /large
为了 快速测试代码,可以先使用文件夹 / movielens /medium 中的小数据集进行测试,这个数据集包含 6000 名用户在 4000 部影片上的 1 百万个评分数据。
本例使用 MovieLens 数据集中的两个文件:“ ratings.dat.” 和“ movies.dat” 。所有的评分数据按照下面的格式存储“ ratings.dat”
UserID :: MovieID :: Rating :: Timestamp
在“ movies.dat” 中以下面的格式存储电影信息
MovieID :: Title :: Genres
针对本例使用一个 standalone 项目模板。假设在用户的环境中,已经配置好所需路径和文件(实例下载地址为 https:// github.com/amplab/training/tree/ampcamp4/machine-learning/scala ), 这些已经在 /root/machine-learning/ scala / 中设置 ,目录 下找到以下 选项:
sbt :包含 SBT 工具的目录。
build.sbt SBT 项目文件。
MovieLensALS.scala :用户需要编译和运行的主要 Scala 主程序。
solution :包含 solution 代码的目录。
用户需要编辑、编译和运行的主要文件是 MovieLensALS.scala ,可以将下面的代码模板拷贝到文件中。


主要运行思路:先在本地编译好可执行jar包,然后spark-submit进行编译运行,中间遇到许多问题,特意记录与大家分享。
编译环境: scala-2.11 + JDK 1.8 + sbt 1.0.3 + spark 集群(注意黑体的版本对应要不然容易出错,有坑)
spark集群:如下图。

实现movielen电影推荐_第1张图片
建立一个sbt工程,其中的sbt,scala,jdk的版本都可以进行选择。

实现movielen电影推荐_第2张图片 然后把代码copy进去,然后修改相关配置,代码如下:
import java.util.Random
import org.apache.log4j.Logger
import org.apache.log4j.Level
import scala.io.Source
import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
import org.apache.spark.SparkContext._
import org.apache.spark.rdd._
import org.apache.spark.mllib.recommendation.{ALS, Rating, MatrixFactorizationModel}
object MovieLensALS{
  def main(args: Array[String]) {
    Logger.getLogger("org.apache.spark").setLevel(Level.WARN)
    Logger.getLogger("org.eclipse.jetty.server").setLevel(Level.OFF)
  }

//  val sparkHome = "/zzti/libs/spark"
//  val master = "local"
  val conf = new SparkConf()
//    .setMaster(master)
//    .setSparkHome(sparkHome)
    .setAppName("MovieLensALS")
    .set("spark.executor.memory", "2g")

//  System.setProperty("hadoop.home.dir", "H:\\大三\\spark\\winutils")
  val sc = new SparkContext(conf)


   //H:\大三\spark\MLib算法\data_movies\ml-1m 本机
  ///movielens/medium/ratings.dat
  val ratings = sc.textFile("/movielens/medium/ratings.dat").map { line =>
    val fields = line.split("::")
    (fields(3).toInt % 10, Rating(fields(0).toInt, fields(1).toInt, fields(2).toDouble))
  }


   //H:\大三\spark\MLib算法\data_movies\ml-1m
   ///movielens/medium/movies.dat
  val movies = sc.textFile("/movielens/medium/movies.dat").map { line =>
    val fields = line.split("::")
    (fields(0).toInt, fields(1))
  }.collect.toMap


  val numRatings = ratings.count
  val numUsers = ratings.map(_._2.user).distinct.count
  val numMovies = ratings.map(_._2.product).distinct.count
  println("Got " + numRatings + " ratings from "
    + numUsers + " users on " + numMovies + " movies.")

  val mostRateMovieIds = ratings.map(_._2.product).countByValue()
    .toSeq
    .sortBy(-_._2)
    .take(50)
    .map(_._1)
  //获它们的id
  val random = new Random(0)
  val seclectedMovies = mostRateMovieIds.filter(x => random.nextDouble() < 0.2)
    .map(x => (x, movies(x))).toSeq
  val myRatings = elicitateRatings(seclectedMovies)
  val myRatingsRDD = sc.parallelize(myRatings)

  val numPartitions = 20
  val training = ratings.filter(x => x._1 < 6).values
    .union(myRatingsRDD).repartition(numPartitions)
    .persist
  val validation = ratings.filter(x => x._1 >= 6 && x._1 < 8).values
    .repartition(numPartitions).persist

  val test = ratings.filter(x => x._1 >= 8).values.persist
  val numTraining = training.count
  val numValidation = validation.count
  val numTest = test.count
  println("Training:" + numTraining + ",validation: " + numValidation + ", test:" + numTest)
  val ranks = List(8, 12)
  val lambdas = List(0.1, 10.0)
  val numIters = List(10, 20)
  var bestModel: Option[MatrixFactorizationModel] = None
  var bestValidationRmse = Double.MaxValue
  var bestRank = 0
  var bestLambda = -1.0
  var bestNumIter = -1
  for (rank <- ranks; lambda <- lambdas; numIter <- numIters) {
    val model = ALS.train(training, rank, numIter, lambda)
    val validationRmse = computeRmse(model, validation, numValidation)
    println("RMSE (validation)=" + validationRmse + "for the model trained with rand =" + rank + ", lambda=" + lambda + ", and numIter= " + numIter + ".")
    if (validationRmse < bestValidationRmse) {
      bestModel = Some(model)
      bestValidationRmse = validationRmse
      bestRank = rank
      bestLambda = lambda
      bestNumIter = numIter
    }
  }
  val testRmse = computeRmse(bestModel.get, test, numTest)
  println("The best model was trained with rank=" + bestRank + " and lambda =" + bestLambda + ", and numIter =" + bestNumIter + ", and itsRMSE on the test set is" + testRmse + ".")
  val myRateMoviesIds = myRatings.map(_.product).toSet
  val candidates = sc.parallelize(movies.keys.filter(!myRateMoviesIds.contains(_)).toSeq)
  val recommendations = bestModel.get.predict(candidates.map((0, _)))
    .collect()
    .sortBy((-_.rating))
    .take(50)
  var i = 1
  println("movies recommended for you:")
  recommendations.foreach { r =>
    println("%2d".format(i) + ":" + movies(r.product))
    i += 1

  }


  /** Compute RMSE (Root Mean Squared Error). */
  def computeRmse(model: MatrixFactorizationModel, data: RDD[Rating], n: Long) = {
    val predictions: RDD[Rating] = model.predict(data.map(x => (x.user, x.product)))
    val predictionsAndRatings = predictions.map(x => ((x.user, x.product), x.rating))
      .join(data.map(x => ((x.user, x.product), x.rating)))
      .values
    math.sqrt(predictionsAndRatings.map(x => (x._1 - x._2) * (x._1 - x._2)).reduce(_ + _) / n)
  }





  /** Elicitate ratings from command-line. */
  def elicitateRatings(movies: Seq[(Int, String)]) = {
    val prompt = "Please rate the following movie (1-5 (best), or 0 if not seen):"
    println(prompt)
    val ratings = movies.flatMap { x =>
      var rating: Option[Rating] = None
      var valid = false
      while (!valid) {
        print(x._2 + ": ")
        try {
          val r = Console.readInt
          if (r < 0 || r > 5) {
            println(prompt)
          } else {
            valid = true
            if (r > 0) {
              rating = Some(Rating(0, x._1, r))
            }
          }
        } catch {
          case e: Exception => println(prompt)
        }
      }
      rating match {
        case Some(r) => Iterator(r)
        case None => Iterator.empty
      }
    }
    if (ratings.isEmpty) {
      error("No rating provided!")
    } else {
      ratings
    }

  }
}
然后运行结果报错2个:NoClassDefFoundError,以及找不到winutils.exe(不影响程序)
实现movielen电影推荐_第3张图片
解决方案:因为版本错误,jdk 1.8与高版本的scala不兼容,所以在build.sbt里面修改scala版本号为2.11.8如下图然后重新build 一下 ok解决。
实现movielen电影推荐_第4张图片
然后就可以打可执行jar包了,如图用idea打jar包
选择project Structure 在选择Atifacts->jar ->from modules with denpencies (不写了网上太多了, 具体百度吧),注意一下Main Class 要与Object 的scala 对象一致、还有MENIFEST.MF这一项要放到src->main里面,如果重复打jar,要删除之后  重新打。
实现movielen电影推荐_第5张图片 实现movielen电影推荐_第6张图片
实现movielen电影推荐_第7张图片
然后开始运行集群,进到spark->bin里面执行  ./spark-submit --class MovieLensALS  movies3.jar 
然后报错如下,最后查询后解决把jar包,用解压缩打开然后把里面的 将打好包的jar文件中的 META-INF/*.RSA META-INF/*.DSA META-INF/*.SF 文件删掉.完美解决。
实现movielen电影推荐_第8张图片
效果如下:
实现movielen电影推荐_第9张图片







你可能感兴趣的:(spark)