创新实训(41)——在Springboot项⽬中使⽤Spark的ALS算法实现协同过滤推
荐
前⾔
昨晚本来想把这部分的博客内容,完成的,结果只写到了设计,时间就不早了,今天把具体的实现,还有实现过程中所遇到的所有的问题写在这⾥。
引⼊依赖
这次我⽤了Spark2.x的java api,并且了解到spark底层是scala实现了,然后上层的api有scala版本和java版本,这⾥我使⽤了它提供的java的api,并且java底层调⽤的函数都是scala实现的,⾮常的⽅便,可以与java进⾏⽆缝的操作。
spark MLlib依赖:
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-mllib_2.12</artifactId>
<version>2.4.4</version>
<exclusions>
<exclusion>
<groupId&le.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
</exclusions>
</dependency>
使⽤SparkSession链接Spark集
//初始化spark初始环境
SparkSession spark = SparkSession.builder().master("local").appName("BlogStormApp").getOrCreate();
我们连接的是本地的master节点,在连接时出现了缺少依赖的问题
经过查阅资料之后,添加了两个依赖
<dependency>
<groupId&le.guava</groupId>
<artifactId>guava</artifactId>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId&dehaus.janino</groupId>
<artifactId>janino</artifactId>
<version>3.0.8</version>
</dependency>
这连个依赖不是我们写的代码使⽤的,好像是Spark内部调⽤的⽅法使⽤的,缺少的话确实程序会运⾏失败的。
模型的训练
(1)使⽤spark的⽅法读取csv数据,然后序列化为javaRDD的格式
/** 内部序列化的静态类
* 数据模型⽤户分类点击量
*/
public static class Rating implements Serializable{
private int userId;
private int itemId;
private int rating;
public static Rating parseRating(String str){
// str = place("\"","");
String[] strArr = str.split(",");
int userId = Integer.parseInt(strArr[0]);
int itemId = Integer.parseInt(strArr[1]);
int rating = Integer.parseInt(strArr[2]);
springboot推荐算法return new Rating(userId,itemId,rating);
}
public Rating()
{
}
public Rating(int userId,int itemId,int rating){
this.userId = userId;
this.itemId = itemId;
this.rating = rating;
}
public int getUserId(){
return userId;
}
public int getItemId(){
return itemId;
}
public int getRating(){
return rating;
}
}
JavaRDD<String> csvFile = ad().textFile(DATA_DIR).toJavaRDD();
JavaRDD<Rating> ratingJavaRDD = csvFile.map(new Function<String, Rating>(){
@Override
public Rating call(String s){
return Rating.parseRating(s);
}
});
(2)将JavaRDD的格式转化为DataFrame的格式(DataFrame中的列可以是存储的⽂本,特征向量,真实标签和预测的标签等),然后切分训练集和测试集,⽅便验证模型。
然后调整参数,调⽤⽅法进⾏模型的训练
Dataset<Row> rating = ateDataFrame(ratingJavaRDD, Rating.class);
//将所有的rating数据分成82分
Dataset<Row>[] splits = rating.randomSplit(new double[]{0.8,0.2});
Dataset<Row> trainingData = splits[0];
Dataset<Row> testingData = splits[1];
//5个特征,
//解决过拟合:1增⼤数据规模,减少rank,增⼤正则化系数
//⽋拟合:增加rank,减少正则化系数
ALS als =new ALS().setMaxIter(10).setRank(5).setRegParam(0.01).setUserCol("userId")
.setItemCol("itemId").setRatingCol("rating");
ALSModel alsModel = als.fit(trainingData);
//模型评测将测试集使⽤模型做⼀次转化的预测
Dataset<Row> predictions = ansform(testingData);
//rmse 均⽅根误差,预测值与真实值的偏差的平⽅除以观测次数,开个根号
RegressionEvaluator evaluator =new RegressionEvaluator().setMetricName("rmse")
.setLabelCol("rating").setPredictionCol("prediction");
double rmse = evaluator.evaluate(predictions);
System.out.println("rmse"+ rmse);
(3)训练结果以及参数的调整(使⽤均⽅根误差来衡量)
均⽅根误差是预测值与真实值偏差的平⽅与观测次数n⽐值的平⽅根,在实际测量中,观测次数n总是有限的,真值只能⽤最可信赖(最佳)值来代替。精密度。这正是标准误差在⼯程测量中⼴泛被采⽤的原因。因此,标准差是⽤来衡量⼀组数⾃⾝的离散程度,⽽均⽅根误差是⽤来衡量观测值同真值之间的偏差,它们的研究对象和研究⽬的不同,但是计算过程类似 。
参数调整的原则:
解决过拟合:1增⼤数据规模,减少rank,增⼤正则化系数
⽋拟合:增加rank,减少正则化系数
ALS als =new ALS().setMaxIter(10).setRank(5).setRegParam(0.01).setUserCol("userId")
.setItemCol("itemId").setRatingCol("rating");
当rank为5 正则化系数为0.01时的结果:
ALS als =new ALS().setMaxIter(10).setRank(7).setRegParam(0.01).setUserCol("userId")
.
setItemCol("itemId").setRatingCol("rating");
当rank为7 正则化系数为0.01时的结果(发现提升rank值会增⼤误差,所以需要减少rank值):
ALS als =new ALS().setMaxIter(10).setRank(4).setRegParam(0.01).setUserCol("userId")
.setItemCol("itemId").setRatingCol("rating");
当rank为4 正则化系数为0.01时的结果(减⼩rank值之后,发现误差变⼩):
ALS als =new ALS().setMaxIter(10).setRank(3).setRegParam(0.02).setUserCol("userId")
.setItemCol("itemId").setRatingCol("rating");
当rank为3 正则化系数为0.02时的结果(提升正则化系数,误差变⼤了,所以降低⼀下正则化系数):
ALS als =new ALS().setMaxIter(10).setRank(4).setRegParam(0.005).setUserCol("userId")
.setItemCol("itemId").setRatingCol("rating");
当rank为4 正则化系数为0.005时的结果(减少正则化系数 误差经⼀步提⾼ ):
ALS als =new ALS().setMaxIter(10).setRank(3).setRegParam(0.01).setUserCol("userId")
.setItemCol("itemId").setRatingCol("rating");
当rank为3 正则化系数为0.01时的结果( rank为3时 误差⽐rank为4⼤,所以重新调整rank):
ALS als =new ALS().setMaxIter(10).setRank(4).setRegParam(0.009).setUserCol("userId")
.setItemCol("itemId").setRatingCol("rating");
当rank为4 正则化系数为0.09时的结果( 重新调整rank为4 正则化系数为0.09,效果不错):
ALS als =new ALS().setMaxIter(10).setRank(4).setRegParam(0.07).setUserCol("userId")
.setItemCol("itemId").setRatingCol("rating");
当rank为4 正则化系数为0.07时的结果( 正则化系数为0.07,误差变⼤):
ALS als =new ALS().setMaxIter(10).setRank(4).setRegParam(0.1).setUserCol("userId")
.setItemCol("itemId").setRatingCol("rating");
当rank为4 正则化系数为0.1时的结果( 正则化系数为0.1,误差变⼤):
所以还是rank为4,且正则化系数为0.09⽐较合适
(4)模型的存储
/**
* 删除⽂件夹下所有⽂件
*
* @param file
*/
private static void deleteFile(File file){
//判断是否为⽂件,是,则删除
if(file.isFile()){
file.delete();
}else{//不为⽂件,则为⽂件夹
/
/获取⽂件夹下所有⽂件相对路径
String[] childFilePath = file.list();
for(String path : childFilePath){
File childFile =new AbsoluteFile()+"/"+ path);
//递归,对每个都进⾏判断
deleteFile(childFile);
}
file.delete();
}
}
//删除⽂件夹下所有⽂件
File file =new File(MODEL_DIR);
deleteFile(file);
//缓存模型
alsModel.save(MODEL_DIR);
读取模型进⾏预测
(1)模型的读取
//初始化spark运⾏环境
SparkSession spark = SparkSession.builder().master("local").appName("BlogStormApp").getOrCreate();
//将模型加载到内存之中
ALSModel alsModel = ALSModel.load(MODEL_DIR);
(2)进⾏预测
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论