Spark MLlib应用
实验目的
深入理解和掌握Spark MLlib的操作方法;理解Spark MLlib编程解决实际问题的方法。
实验要求
- 掌握基于Spark MLlib的Scala编程环境配置;
- 掌握Spark MLlib的操作编程方法。
实验内容
- 参考实验四创建一个支持Spark SQL的项目
- 在Maven中配置Spark MLlib编程环境,pom.xml中添加:
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-mllib_2.12</artifactId>
<version>3.0.1</version>
</dependency>
*若安装的Spark不是3.0.1,则自己搜索适合的spark-mllib版本
3. 特征工程:基于实验四的结果进行搜索,构造用户、商品和评分数据用于协同过滤商品推荐算法
(1) 按用户ID和商品ID共同分组查询,并统计用户购买该商品的次数(不是数量)
提示:groupBy(“CustomerID”, “StockCode”).count
评分逻辑:将用户对商品的购买和重复购买行为视为对商品的评分
思考:自己设计其他的评分逻辑并实现
df.groupBy("CustomerID", "StockCode").count.show()
(2) 将用户ID、商品ID和用户购买商品次数分别投影到userID、itemID和rating字段
提示:.selectExpr(“hash(CustomerID) as userID”, " hash (StockCode) as itemID", “count as rating”)
通过Spark SQL搜索生成一个DataFrame,包含userID、itemID和rating三个字段,userID和itemID均用Spark SQL Hash函数转为整型数,ALS模型不支持字符串类型输入
df.selectExpr("hash(CustomerID) as userID", " hash (StockCode) as itemID", "count as rating").show()
(3) 按8:2的比例将第(2)步的结果数据划分为训练集和测试集
val Array(training, test) = df.randomSplit(Array(0.8, 0.2))
Training data
Test data
- 模型训练:训练协同过滤模型用于商品推荐
(4) 创建一个协同过滤模型,并将用户列、商品列和评分列分别设为第(2)步中的列名,并设定其他的模型参数
提示:
val als = new ALS()
.setUserCol( … ) //设置用户字段名
.setItemCol( … ) //设置商品字段名
.setRatingCol( … ) //设置评分字段名
.setMaxIter(5) //设置最大迭代次数
.setRegParam(0.01) //设置惩罚系数
(5) 送入训练集数据训练模型
val model = als.fit(training)
模型评估
(6) 将测试集数据送入模型进行模型评估,将均方根误差保存至本地文件
提示:model.setColdStartStrategy(“drop”) //冷启动策略设为drop以免出现nan值
val predictions = model.transform(test)
val evaluator = new RegressionEvaluator() //创建评估器
.setMetricName("rmse")
.setLabelCol("rating")
.setPredictionCol("prediction")
val rmse = evaluator.evaluate(predictions) //执行评估并返回均方误差
val writer = new PrintWriter(new File("……")) //使用Scala方法保存至本地
writer.println(rmse) //保存均方误差值
writer.close()
(7) 为每个用户生成三个商品推荐(DataFrame),执行搜索将推荐结果(结构体数组)使用集合函数explode展开为多行数据并持久化为JSON文件
提示:val userRecs = model.recommendForAllUsers(3)
userRecs.selectExpr(“userID”,“explode(recommendations.itemID) as itemID”)
持久化参见PPT第5章6.3节
思考:这里文件中保存的UserID和ItemID均为hash值(在3(2)步时转为了hash值),请将其保存为真实的用户ID和商品ID
userRecs.write.json("hdfs://主机名或ip地址:端口号/json文件名")
保存结果预览
编译和执行:
按实验二中的方法编译打包并执行程序
实验参考文献:
http://spark.apache.org/docs/latest/ml-collaborative-filtering.html
代码
package cn.edu.swpu.scs
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.ml.recommendation.ALS
import org.apache.spark.sql.SparkSession
import org.apache.spark.ml.evaluation.RegressionEvaluator
import java.io.{PrintWriter, File}
object MLlib {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("Spark SQL").setMaster("local[*]")
val sc = new SparkContext(conf)
val spark = SparkSession.builder().master("local[*]").appName("Spark SQL").getOrCreate()
val df = spark.read.json("hdfs://主机名或ip地址:端口号/json文件名")
val df_temp = df.groupBy("CustomerID", "StockCode").count
val df_final = df_temp.selectExpr("hash(CustomerID) as userID", " hash (StockCode) as itemID", "count as rating")
val Array(training, test) = df_final.randomSplit(Array(0.8, 0.2))
val als = new ALS()
.setUserCol("userID") //设置用户字段名
.setItemCol("itemID") //设置商品字段名
.setRatingCol("rating") //设置评分字段名
.setMaxIter(5) //设置最大迭代次数
.setRegParam(0.01) //设置惩罚系数
val model = als.fit(training)
model.setColdStartStrategy("drop") //冷启动策略设为drop以免出现nan值
val predictions = model.transform(test)
val evaluator = new RegressionEvaluator() //创建评估器
.setMetricName("rmse")
.setLabelCol("rating")
.setPredictionCol("prediction")
val rmse = evaluator.evaluate(predictions) //执行评估并返回均方误差
val writer = new PrintWriter(new File("本地文件名")) //使用Scala方法保存至本地
writer.println(rmse) //保存均方误差值
writer.close()
val userRecs = model.recommendForAllUsers(3)
userRecs.selectExpr("userID","explode(recommendations.itemID) as itemID")
userRecs.write.json("hdfs://主机名或ip地址:端口号/json文件名")
}
}
实验感悟
唯一遇到的问题就是创建als变量的时候,有点问题。
之前是这样导入的包
import org.apache.spark.mllib.evaluation.RegressionEvaluator
后将mllib改为ml就好了
import org.apache.spark.ml.evaluation.RegressionEvaluator
Spark中ml和mllib的主要区别和联系如下:
ml和mllib都是Spark中的机器学习库,目前常用的机器学习功能2个库都能满足需求。
spark官方推荐使用ml, 因为ml功能更全面更灵活,未来会主要支持ml,mllib很有可能会被废弃(据说可能是在spark3.0中deprecated)。
ml主要操作的是DataFrame, 而mllib操作的是RDD,也就是说二者面向的数据集不一样。相比于mllib在RDD提供的基础操作,ml在DataFrame上的抽象级别更高,数据和操作耦合度更低。
DataFrame和RDD什么关系?DataFrame是Dataset的子集,就是Dataset[Row], 而DataSet是对RDD的封装,对SQL之类的操作做了很多优化。
相比于mllib在RDD提供的基础操作,ml在DataFrame上的抽象级别更高,数据和操作耦合度更低。
ml中的操作可以使用pipeline, 跟sklearn一样,可以把很多操作(算法/特征提取/特征转换)以管道的形式串起来,然后让数据在这个管道中流动。大家可以脑补一下Linux管道在做任务组合时有多么方便。
ml中无论是什么模型,都提供了统一的算法操作接口,比如模型训练都是fit;不像mllib中不同模型会有各种各样的trainXXX。
mllib在spark2.0之后进入维护状态, 这个状态通常只修复BUG不增加新功能。
我认为可能就是ml主要操作是DataFrame的原因