基于Spark ALS的离线推荐系统实践

来源:互联网 发布:freehand 11 for mac 编辑:程序博客网 时间:2024/05/01 02:20

首先,此推荐系统搭建在基于Hadoop(2.7.3)、Spark(2.0.0)、Zookeeper(可选)的集群上,关于Hadoop和Spark集群搭建可参考我前面几篇水水的博文~

其次,此demo用到的数据来自DataCastle的一个正在进行的比赛,数据集在此请大家移步下载~比赛的主题是关于地点的推荐
(训练集数据格式:userID, addressID, count –> 用户id,地点id,访问次数)

Spark机器学习库MLlib中关于推荐的算法至今有且只有一种:ALS(交替最小二乘法),不同于协同过滤求用户间相似度,而是一种基于矩阵分解的算法,关于该算法的来龙去脉各位可自行搜索~

废话不多说,上代码说话
Step 1:在IntelliJ IDEA构建maven工程,则pom.xml文件中依赖库具体如下:

<dependency>            <groupId>org.apache.spark</groupId>            <artifactId>spark-core_${scala.version}</artifactId>            <version>${spark.version}</version>        </dependency>        <dependency>            <groupId>org.apache.spark</groupId>            <artifactId>spark-sql_${scala.version}</artifactId>            <version>${spark.version}</version>        </dependency>        <dependency>            <groupId>org.apache.spark</groupId>            <artifactId>spark-streaming_${scala.version}</artifactId>            <version>${spark.version}</version>        </dependency>        <dependency>            <groupId>org.apache.spark</groupId>            <artifactId>spark-mllib_${scala.version}</artifactId>            <version>${spark.version}</version>        </dependency>        <dependency>            <groupId>com.databricks</groupId>            <artifactId>spark-csv_${scala.version}</artifactId>            <version>1.5.0</version>        </dependency>

我系统安装的Scala版本是2.11.6,各位根据实际情况调整~

Step 2:在工程中构建一个Scala Module,如图:
这里写图片描述
备注:配置ScalaSDK步骤:File –> Project Structure –> Project Settings –> Libraries –> 这里写图片描述

Step 3:下载数据集,上传数据集到HDFS

hdfs dfs -put /保存文件的HDFS路径 /本地文件路径

Step 4:新建.scala文件,写代码:
1)创建SparkSession,从HDFS导入数据Dataset:

    val conf = new SparkConf()      .setAppName("Place Recommendation APP")      .setMaster("spark://master:7077")      .set("spark.rpc.netty.dispatcher.numThreads","2") //预防RpcTimeoutException    val spark = SparkSession.builder()          .config(conf).getOrCreate()    //导入数据    val base = "/user/datacastle/data/"    val rawUserAddressData = spark.read.textFile(base + "train.txt")    val rawAddressData = spark.read.textFile(base + "mappedpoiinfo.txt")

2)数据预处理,提取有效信息(相对简单,具体看代码),并将Dataset[String]转为DataFrame:
2.1>train.txt的训练集数据转成DataFrame(一个DataFrame可看成是关系型数据库中的一张表):

/**    * user,address,count原始文本数据转为DataFrame    *    * @param rawUserAddressData    * @return    */  def buildCounts(rawUserAddressData: Dataset[String]): DataFrame = {    rawUserAddressData.map{ line =>      val Array(userID, addressID, count) = line.split(',').map(_.toInt)      (userID, addressID, count)    }.toDF("user", "address", "count")  }

2.2>获取所有有效用户id:

/**    * 获取所有用户id,升序排序    * @param rawUserAddressData    */  def buildUserByID( rawUserAddressData: DataFrame ): Array[Any] = {    rawUserAddressData.groupBy("user").max("user").orderBy("user").rdd.map{line => line(0)}.collect()  }

3)重头戏:调用Spark MLlib中ALS模型并设置参数进行推荐:
3.1>ALS模型参数设置以及导入训练集训练模型:

val model = new ALS()      .setSeed(Random.nextLong())      .setImplicitPrefs(true)      .setRank(10)      .setRegParam(1.0)      .setAlpha(40.0)      .setMaxIter(20)      .setUserCol("user")      .setItemCol("address")      .setRatingCol("count")      .setPredictionCol("prediction")      .fit(trainData) //fit内部已经将trainData转成Rating,并且调用了train

3.2>给指定用户推荐Top N个地点:

/**    * 推荐top howmany个地点    *    * @param model    * @param userID    * @param howMany    * @return    */  def makeRecommendations(model: ALSModel, userID: Int, howMany: Int): DataFrame = {    val toRecommend = model.itemFactors      .select($"id".as("address"))      .withColumn("user", lit(userID))    model.transform(toRecommend)      .select("user", "address")      .orderBy($"prediction".desc)      .limit(howMany)  }

3.3>给所有用户各自推荐不同的50个地点:

val Recommendations = makeRecommendations(model, userIDs(0).toString.toInt, 50)    var result: DataFrame = Recommendations    for (i <- 1 until userIDs.length) {      val userID = userIDs(i).toString.toInt      val topRecommendations = makeRecommendations(model, userID, 50)      result = result.union(topRecommendations)      println("<------------------------>" + "have finished the index of : " + i + "<------------------------>")    }

3.4>保存结果,转成.csv文件保存到HDFS:

    val csvData = result.select("user", "address")    csvData.coalesce(1)      .write      .format("com.databricks.spark.csv")      .option("header", "true")      .save("/user/datacastle/data/cand1.csv")

注意:此处使用了databricks社区的开源库spark-csv,重点在于:由于csvData是由多个DataFrame组合union而来,所以分布在n个partition中,如果需要把所有结果都保存在同一个csv中,需要repartition一下,所以调用了coalesce(1),将n个partition归一

3.5>最后释放内存:

trainData.unpersist()model.userFactors.unpersist()model.itemFactors.unpersist()

Step 5:编译工程(Make Project),打成jar包(Build Artifacts),需要注意的是:
在Project Structure中设置Artifacts,需要把jar包的依赖全部去掉,并设置好main class,如图:
这里写图片描述

Step 6:提交*.jar到spark集群,此处采用standalone模式,跑起来~

$SPARK_HOME/bin/spark-submit --class placeRecommender ./***.jar

最后PO上完整代码,欢迎大神们纠bug指教~
完整代码下载

1 0