《智能推荐技术与应用》课程作品(项目)报告
水院的同学不要抄袭呀!
1 作品(项目)目标
与搜索引擎不同,推荐系统并不需要用户提供明确的需求,而是通过分析用户的历史行为,主动为用户推荐能够满足他们兴趣和需求的信息。为了能够更好地满足用户需求,需要依据其网站的海量数据,研究用户的兴趣偏好,分析用户的需求和行为,发现用户的兴趣点,从而引导用户发现自己的信息需求,将长尾网页(长尾网页是指网页的点击情况满足长尾理论中尾巴部分的网页)准确地推荐给所需用户,即使用推荐引擎来为用户提供个性化的专业服务。
目标:
1.按地域研究用户访问时间、访问内容、访问次数等分析主题,深入了解用户访问网站的行为、目的及关心的内容(主要指统计信息)。
2.借助大量用户访问记录,使用推荐算法发现用户访问习惯,对不同用户推荐相关服务页面。
2 作品(项目)方案设计
2.1.1对用户的数据分析
属性名称 属性说明 属性名称 属性说明
userID 用户ID pagePath 路径
timestamp 时间戳 FullReferrerURL 入口网址
2.1.2根据推荐算法进行建模评测
协同过滤推荐( Collaborative Filtering Recommendation )技术是推荐系统中应用最早和最为成功的技术之一。它一般采用最近邻技术,利用用户的历史喜好信息计算用户之间的距离,然后利用目标用户的最近邻居用户对商品评价的加权评价值来预测目标用户对特定商品的喜好程度,系统从而根据这一喜好程度来对 目标用户进行推荐。协同过滤推荐的最大优点是对推荐对象没有特殊的要求,能处理非结构化的复杂对象,如音乐、电影。
2.1.3根据模型得出推荐结果。
当经过评估,选定推荐模型后,则可以应用模型对所有用户生成推荐数据。
3 智能推荐建模及模型评测
3.1 数据探索
源数据表结构及说明如表3-1:
表 31 网站日志数据属性及其说明
属性名称 属性说明 属性名称 属性说明
realIP 真实ip fullURLID 网址类型
realAreacode 地区编号 hostname 源地址名
userAgent 浏览器代理 pageTitle 网页标题
userOS 用户浏览器类型 pageTitleCategoryId 标题类型ID
userID 用户ID pageTitleCategoryName 标题类型名称
clientID 客户端ID pageTitleKw 标题类型关键字
timestamp 时间戳 fullReferrrer 入口源
timestamp_format 标准化时间 FullReferrerURL 入口网址
pagePath 路径 organicKeyword 搜索关键字
ymd 年月日 source 搜索源
fullURL 网址
3.1.0 数据来源
从已有的数据读取,然后转为临时表。
代码3-1-0
//读取数据
val data = spark.read.csv(“G:\python和智能推荐实战\智能推荐发学生部分\lawdata_20140819_20141015.csv”)
//转为临时表
data.registerTempTable(“law”)
3.1.1 网页类型统计
统计内容为:网页类型、记录数及其所占总记录百分比。
代码 3-1-1 按网页类型统计
----统计网页类型
spark.sql("
select substring(_c11,1,3) as page_type,
count() as count_num,
round((count()/837450.0)*100,4) as weights
from law group by substring(_c11,1,3)
order by count_num desc").show()
运行结果如表 3-1-1-1所示,从中发现点击与咨询相关的网页(网页类型为101,http://www..cn/ask/)的记录的占比为49.15%,其次是知识相关网页(网页类型为107,http://www..com/info/)占比约为21.84%,剩余其他的类型网页(网页类型为199)占24.05%左右。
表3-1-1-1
±--------±--------±------+
|page_type|count_num|weights|
±--------±--------±------+
| 101| 411665|49.1570|
| 199| 201426|24.0523|
| 107| 182900|21.8401|
| 301| 18430| 2.2007|
| 102| 17357| 2.0726|
| 106| 3957| 0.4725|
| 103| 1715| 0.2048|
±--------±--------±------+
3.1.2 咨询类别内部统计
统计内容为:101网页类型的子类型、记录数及其所占101网页类型总记录百分比。
代码 3-1-2 咨询类内部统计
----咨询类别内部统计
spark.sql(“select substring(_c11,1,6) as page_type,
count() as count_num,
round((count()/411665.0)*100,4) as weights
from law where substring(_c11,1,3) =101
group by substring(_c11,1,6)”).show()
其结果如表3-1-2-1所示。其中浏览咨询内容页(101003)记录最多,其次是咨询列表页(101002)和咨询首页(101001)。结合上述初步结论,可以得出用户都喜欢通过浏览问题的方式找到自己需要的信息,而不是以提问的方式或者查看长篇的知识的方式。
表3-1-2-1
±--------±--------±------+
|page_type|count_num|weights|
±--------±--------±------+
| 101009| 854| 0.2075|
| 101005| 63| 0.0153|
| 101002| 7776| 1.8889|
| 101007| 147| 0.0357|
| 101006| 107| 0.0260|
| 101004| 125| 0.0304|
| 101001| 5603| 1.3611|
| 101003| 396612|96.3434|
| 101008| 378| 0.0918|
±--------±--------±------+
3.1.3 网页中带有“?”记录统计
统计所有访问网页中带有“?”的总记录数。统计分析访问网页中带有?的所有记录中,各网页类型、记录数、占访问网页中带有?的记录总数的百分比。
代码 3-1-3 网页中带有“?”记录统计及各个类别占比
-----统计visiturl中带有?的所有记录。
spark.sql(“select count() as num from law where _c10 like ‘%?%’ ").show()
65492
-----统计带有?的所有记录中,各网页类型所占比例
spark.sql("select substring(_c11,1,7) as page_type,
count(),round((count(*)*100)/ 65492,2) as weights
from law where _c10 like ‘%?%’
group by substring(_c11,1,7)
order by weights desc”).show
其结果整理见表3-1-3-1。包含“?”总记录数为65492,特别在其他网页这一类型中占了98%左右,比重较大,因此需要进一步分析该类型网页的内部规律,但在知识相关与法规专题中的占比仅1%左右。
表3-1-3-1
±--------±-------±------+
|page_type|count(1)|weights|
±--------±-------±------+
| 1999001| 64718| 98.82|
| 301001| 356| 0.54|
| 107001| 346| 0.53|
| 101003| 47| 0.07|
| 102002| 25| 0.04|
±--------±-------±------+
3.2 数据预处理(异常值、缺失值处理,编码,数据集分割等)
3.2.1 筛选数据
把无.html点击行为,中间类型网页(带midques_关键字),网址中带有?的记录,重复数据,法律服务以外的数据过滤掉。
代码 3-2-1 筛选数据
spark.sql("select distinct _c6,_c4,_c10,_c11 from law
where _c10 like ‘%.html%’
and _c10 not like ‘%?%’
and c10 not like '%midques%’
and _c11 not like ‘199%’ ")
根据分析目标以及探索结果可知咨询、知识、法规专题是其主要业务来源,故需筛选咨询、知识与法规专题相关的记录,将此部分数据作为模型分析需要的数据。
3.2.2 数据变换
在3.2.1的基础上继续筛选,规则如下:
1.去掉访问网址中包含browse.html或browse_的记录。
2.如果访问网址中包含ask/question_关键字且ask/question_与.html之间的字符串全为数字,保留;否则ask/question_与.html之间的字符串不全为数字,舍弃。
代码 3-2-2
val a = spark.sql("select distinct _c6,_c4,_c10,_c11 from law
where _c10 like ‘%.html%’
and _c10 not like ‘%?%’
and c10 not like '%midques%’
and _c11 not like ‘199%’
and _c13 not like ‘%法律快车%’
and _c13 not like ‘%律师助手%’
//3.2.2
and _c10 not like ‘%browse.html%’
and c10 not like '%browse%’
and c10 not regexp '^.*?[a-zA-Z0-9]+?\.html$’ ")
//将数据转为rdd
val b = a.rdd
//保存数据
b.repartition(1).saveAsTextFile(“G:\python和智能推荐实战\智能推荐发学生部分\部分数据2\cleandata”)
结果如下: b.take(1) //(时间戳,用户ID,url,url类型)
Array([1422860000000,268392030.1,http://www.*.cn/info/shuifa/slb/2012111978933.html,107001]
3.2.3 数据编码
编码思路为:1)求得原始用户以及URL的去重值,并按照ASCII值进行排序;2)使用排序后的原始用户以及URL的下标值来代替该用户或URL;3)使用编码后的值替换原始数据中的值。使用Spark对原始数据进行上述编码处理,其代码如3-3-1所示。
代码清单 3-3-1 基于3.2.2的数据编码及替换
//加载数据, 原始数据
val dataAll = sc.textFile(“G:\python和智能推荐实战\智能推荐发学生部分\部分数据2\cleandata”).map{x => val fields=x.split(“,”); (fields(0),fields(1),fields(2),fields(3))}
// 排序去重后用户、URL数据
val userUrl = dataAll.map(x => (x._2,x._3))
val allUserList = userUrl.map(data=>data._1).distinct.sortBy(x => x)
val allUrlList = userUrl.map(data=>data._2).distinct.sortBy(x => x)
// 构造用户、URL编码
val allUserIdList = allUserList.zipWithIndex.map(data=>(data._1,data._2.toInt))
val allUrlIdList = allUrlList.zipWithIndex.map(data=>(data._1,data._2.toInt))
// 保存编码数据
allUserIdList.map(x => x._1 +“,”+x._2).repartition(1).saveAsTextFile(“G:/python和智能推荐实战/智能推荐发学生部分/部分数据2/law_userlist”)
allUrlIdList.map(x => x._1 +“,”+x._2).repartition(1).saveAsTextFile(“G:/python和智能推荐实战/智能推荐发学生部分/部分数据2/law_urllist”)
// 替换原始数据
val replacedDataAll = dataAll.map(x => (x._2,(x._1,x._3,x._4))).join(allUserIdList).map(x => (x._2._1._2,(x._2._1._1,x._2._2,x._2._1._3))).join(allUrlIdList).map(x => (x._2._1._1,x._2._1._2,x._2._2,x._2._1._3))
// 保存编码后数据
replacedDataAll.saveAsTextFile(“G:/python和智能推荐实战/智能推荐发学生部分/部分数据2/law_data_replaced”)
//数据格式:时间戳,用户编码,url编码,url类型
3.2.3 数据集分割
经过预处理后的数据,再次分为知识类、咨询类和法规类数据,针对每类数据都采用统一的处理方式。
数据首先按照时间戳分为3份,分别是:训练集、验证集和测试集,对应占比为80%、10%、10%。其分割代码如代码清单 419所示。
代码清单 3-2-3 数据分割为训练集、验证集、测试集
// 分割点:编码数据: timestamp,user,url,urlType
val inputpath = “G:\python和智能推荐实战\智能推荐发学生部分\部分数据2\law_data_replaced”
val RatingCodeList = sc.textFile(inputpath,6).map{ x => val fields = x.slice(2,x.size-2).split(“,”);(fields(0).toLong,fields(1).toInt,fields(2).toInt,fields(3).toInt)}.sortBy(_._1)
//添加一列1…开始的数字
val zipRatingCodeList = RatingCodeList .zipWithIndex.mapValues(x =>(x+1))
//定义数据分割点
val totalNum = RatingCodeList.count()
val splitPoint1 = totalNum0.8 toInt
val splitPoint2 = totalNum0.9 toInt
//生成训练集数据
val train = zipRatingCodeList.filter(x =>(x._2<splitPoint1)).map(x =>(x._1._1,x._1._2,x._1._3,x._1._4))
//生成验证集数据
val validate = zipRatingCodeList.filter(x =>(x._2>=splitPoint1 && x._2<splitPoint2)).map(x =>(x._1._1,x._1._2,x._1._3,x._1._4))
//生成测试集数据
val test = zipRatingCodeList.filter(x =>(x._2>=splitPoint2)).map(x =>(x._1._1,x._1._2,x._1._3,x._1._4))
//训练集数据总数
train.count
//验证集数据总数
validate.count
//测试集数据总数
test.count
//存储训练集,验证集,测试集
train.repartition(1).saveAsTextFile(“G:/python和智能推荐实战/智能推荐发学生部分/部分数据2/分割的数据/trainRatings”)
validate.repartition(1).saveAsTextFile(“G:/python和智能推荐实战/智能推荐发学生部分/部分数据2/分割的数据/validateRatings”)
test.repartition(1).saveAsTextFile(“G:/python和智能推荐实战/智能推荐发学生部分/部分数据2/分割的数据/testRatings”)
训练集用于训练模型,验证集用于评估模型以找到最优模型,测试集对最优模型进行验证。
3.3 建立基于用户的协同过滤模型
基于用户的协同过滤,即通过不同用户对项目的评分来评测用户之间的相似性,搜索目标用户的最近邻,然后根据最近邻的评分向目标用户产生推荐。具体描述如下:
1.计算相似度
用户之间的相似度通过每个用户对项目的评分向量(注意,在本文中如果用户对某个URL进行访问,那么该项目就为1,如果没有访问,那么就为0)计算得到。相似度的计算可以使用任何向量相似度计算公式,但在实际使用中,需要选择一种契合模型数据的算法。同时,如果现有相似度计算算法不符合实际情况,也可对其加以改进。
2.寻找与目标用户最近邻的K个用户
在计算出各个用户之间的相似度后,可以找到所有与目标用户的相似度大于某一阈值的近邻用户(第一步粗略过滤),然后对这些用户按照相似度进行排序,得到前K个近邻用户。
3.通过这K个用户进行推荐
得到K个近邻用户后,怎么推荐呢?当然,这里的方式有多种。比如使用相似度和所有K个用户的项目对应加权进行推荐。
根据上述算法原理,编写Spark的基于用户的协同过滤算法,其代码如代码清单 417所示。
代码 3-3 Spark 基于用户的协同过滤算法实现
//3.3模型构建
import scala.math._
// 处理参数
val trainDataPath = “G:\python和智能推荐实战\智能推荐发学生部分\部分数据2\分割的数据\trainRatings”
val modelPath = “G:\python和智能推荐实战\智能推荐发学生部分\部分数据2\存储的模型”
val minItemsPerUser = 2
val recommendItemNum = 10
val splitter = “,”
// 加载训练集数据,
val trainDataRaw= sc.textFile(trainDataPath).map{x=>val fields=x.slice(1,x.size-1).split(splitter); (fields(1).toInt,fields(2).toInt)}
// 获取训练集数据,以单用户最小访问Item数过滤
val trainDataFiltered = trainDataRaw.groupBy(_._1).filter(data=>data._2.toList.size>=minItemsPerUser).flatMap(_._2)
// (user,item)pair 的重复次数统计
val trainUserItemNumPre = trainDataFiltered.countByValue().toArray.map(x=>(x._1._1,(x._1._2,x._2.toInt)))
// user的访问次数统计
val trainUserNumPre = trainDataFiltered.keys.countByValue().toArray
// 转化为RDD
val trainUserItemNum = sc.parallelize(trainUserItemNumPre)
val trainUserNum =sc.parallelize(trainUserNumPre)
// 建立用户相似度矩阵
// (user,item,userItemNum,userSum)
val userItemBase = trainUserItemNum.join(trainUserNum).map(x=>(x._1,x._2._1._1,x._2._1._2.toInt,x._2._2.toInt))
// (item,(user,userItemNum,userSum))
val itemUserBase = userItemBase.map(x=>(x._2,(x._1,x._3,x._4)))
// [(item, ((userA,userAItemNum,userASum), (userB,userBItemNum,userBSum)))]
val itemMatrix = itemUserBase.join(itemUserBase).filter((f => f._2._1._1 < f._2._2._1))
// (userA,userB),(userAItemNum,userASum,userBItemNum,userBSum)
val userSimilarityBase = itemMatrix.map(f=>((f._2._1._1,f._2._2._1),(f._2._1._2,f._2._1._3,f._2._2._2,f._2._2._3)))
// 应用Jaccard 公式求相似度
val userSimilarityPre = userSimilarityBase.map(data => {
val user1=data._1._1
val user2= data._1._2
val similarity = (min(data._2._1, data._2._3))*1.0/(data._2._2 + data._2._4)
((user1, user2), similarity)
}).combineByKey(
x=>x,
(x:Double,y:Double)=>(x+y),
(x:Double,y:Double)=>(x+y))
// 用户相似度 (user,(user,similarity))
val userSimilarity = userSimilarityPre.map(x=>((x._1._2,x._1._1),x._2)).union(userSimilarityPre).
map(x=>(x._1._1,(x._1._2,x._2)))
// 初始化推荐集合(user,List(item,similarity))
val statistics = trainDataFiltered.join(userSimilarity).map(x=>(x._2._2._1,(x._2._1,x._2._2._2))).combineByKey(
(x:(Int,Double)) => List(x),
(c:List[(Int,Double)], x:(Int,Double)) => c :+ x ,
(c1:List[(Int,Double)], c2:List[(Int,Double)]) => c1 ::: c2)
//生成推荐集合(user,List(item))
//为每个user,截取前recommendItemNum个item记录
val dataModel = statistics.
map(data=>{val key = data._1; val value = data._2.sortWith(_._2>_._2);
if(value.size>recommendItemNum){
(key,value.slice(0,recommendItemNum))
}else{(key,value)}}).
map(x=>(x._1,x._2.map(x=>x._1)))
// 存储模型
dataModel.repartition(1).saveAsObjectFile(modelPath)
println("Model saved")
sc.stop()
模型结果:dataModel.take(5)
Array[(Int, List[Int])] = Array((4632,List(9848, 9842)),
(10656,List(10403, 5977, 10403, 5223, 10403, 9120, 10403, 6685, 10403, 1341)), (5496,List(1917, 1917, 2038, 2409)),
(5784,List(8318, 8513, 1537, 8513, 8508, 8513, 8513, 8512, 8318, 8513)), (5976,List(4253, 4273, 4253, 4269, 4253, 4381, 4352, 4253, 4253, 4277)))
3.4 模型评测(选取常用评测指标实现)
好的推荐系统能够满足用户的需求,推荐其感兴趣但不全是热门的物品,同时也需要用户反馈意见帮助完善其推荐系统。因此,好的推荐系统不仅能预测用户的行为,而且能帮助用户发现可能会感兴趣,但却不易被发现的物品。同时,推荐系统还应该帮助商家将长尾中的好商品发掘出来,推荐给可能会对它们感兴趣的用户。在实际应用中,评测推荐系统是必不可少的。评测指标主要来源于如下3种评测推荐效果的实验方法,即离线测试、用户调查和在线实验。
离线测试是通过从实际系统中提取数据集,然后采用各种推荐算法对其进行测试,获各个算法的评测指标。这种实验方法的好处是不需要真实用户参与。
基于用户的协同过滤推荐模型评测,加载推荐模型与测试数据集,为每个用户选取k个推荐结果,与测试数据集的实际记录进行匹配。具体实现过程如代码如3-4所示:
代码 3-4 模型评测
//模型评测
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
/**
- 评估基于用户的协同过滤模型,需要输入以下参数
- testDataPath: 测试数据(userid,itemid)
- modelPath:模型存储目录
- minRatedNumPerUser:单用户评价菜品的最小次数
- kList:推荐数量K值列表
- resultPath:评估结果的存储目录
- splitter:输入原始数据分隔符
*/
// 匹配输入参数
val testDataPath = “G:\python和智能推荐实战\智能推荐发学生部分\部分数据2\分割的数据\testRatings”
val modelPath = “G:\python和智能推荐实战\智能推荐发学生部分\部分数据2\存储的模型”
val minRatedNumPerUser = 2
val kList = “30,50,70,90,110”.split(“,”).map(_.toInt)
val resultPath = “G:\python和智能推荐实战\智能推荐发学生部分\部分数据2\评估结果1”
val splitter = “,”
// 加载推荐模型,为每个user抽取前K(最大值)个item记录
val dataModel:RDD[(Int, List[(Int)])] = sc.objectFile[(Int, List[(Int)])](modelPath)
val searchResult = dataModel.map(x=>(x._1,x._2.take(kList.max))).cache()
// 加载测试集记录
val dataTest = sc.textFile(testDataPath).map{x=>val fields=x.slice(1,x.size-1).split(splitter); (fields(1).toInt,fields(2).toInt)
}.distinct
println("test records: " + dataTest.count() )
// 过滤测试集记录 (user,item)
val testData = dataTest.groupBy(x=>x._1).filter(x=>x._2.size>=minRatedNumPerUser).flatMap(x=>x._2)
val sharedUserIds = testData.keys.distinct.intersection(dataModel.keys.distinct).collect.toList
println("shared user records: " + sharedUserIds.size )
val testUserRecords = testData.filter(data=>sharedUserIds.contains(data._1))
println("filtered test records: " + testUserRecords.count() )
val testUserRated = testUserRecords.combineByKey(
(x:Int) => List(x),
(c:List[Int], x:Int) => x :: c ,
(c1:List[Int], c2:List[Int]) => c1 ::: c2).cache()
// 以模型的推荐item与测试集的item,进行匹配比较
// 计算不同的K值下的recall, precision值
val results = for ( k <- kList ;
// 与测试集比较,获得matched记录数:
val initResult = testUserRated.join(searchResult.map(x=>(x._1,x._2.take(k))));
val finalResult = initResult.map(x => (x._1,x._2._1.size,x._2._2.size,x._2._1.intersect(x._2._2).size));
val matchedNum = finalResult.map(x=>(x._4)).sum.toInt;
val recall_precesion = finalResult.collect.map(x => (x._4.toDouble/x._2,x._4.toDouble/x._3));
val real_recall_precesion = recall_precesion.reduceLeft((x,y) => (x._1+y._1,x._2+y._2))
) yield (k,recall_precesion.size,matchedNum,
real_recall_precesion._1 * 100/ recall_precesion.size ,
real_recall_precesion._2 * 100/ recall_precesion.size )
//存储结果
sc.parallelize(results.map(x => x._1 + "," + x._2 + "," + x._3+ "," + x._4+ "," + x._5)).repartition(1).saveAsTextFile(resultPath)
println("Evaluation completed.")
sc.stop()
在另外一些电子商务网站中,用户只有二元选择,比如喜欢和不喜欢,浏览与否等。针对这类型的数据预测,就要用分类准确度,其中的评测指标有准确率(P),它表示用户对一个被推荐产品感兴趣对的可能性。召回率®表示一个用户喜欢的产品被推荐的概率。F1指标综合考虑了准确率与召回率因素,能更好地评价算法的优劣(F1越大,说明算法越优)。
评测结果如下:
模型 模型参数 k值 召回率(R) 精确率§ 综合指标F1
基于用户的协同过滤算法模型 单用户评价过的最小物品数=2 10 8 3.7015873015873013 1.1999999999999997
30 8 3.7015873015873013 1.1999999999999997
50 8 3.7015873015873013 1.1999999999999997
70 8 3.7015873015873013 1.1999999999999997
90 8 3.7015873015873013 1.1999999999999997
4 实现推荐结果
4.1.1 对所有用户进行法律服务网址推荐
当经过评估,选定推荐模型后,则可以应用模型对所有用户生成推荐数据。
代码 4-1-1 实现推荐结果
//设置编码数据集的路径
val law_userlist= “G:\python和智能推荐实战\智能推荐发学生部分\部分数据2\law_userlist”
val law_urllist= “G:\python和智能推荐实战\智能推荐发学生部分\部分数据2\law_urllist”
//加载用户编码数据集,菜品编码数据集
val splitter = “,”
“G:\python和智能推荐实战\智能推荐发学生部分\部分数据2\law_userlist\part-00000”
“G:\python和智能推荐实战\智能推荐发学生部分\部分数据2\law_urllist\part-00000”
val law_userlist1= sc.textFile(“G:\python和智能推荐实战\智能推荐发学生部分\部分数据2\law_userlist\part-00000”).map{x =>val fields = x.split(splitter);(fields(0),fields(1).toInt)}
val law_urllist1= sc.textFile(“G:\python和智能推荐实战\智能推荐发学生部分\部分数据2\law_urllist\part-00000”).map{x => val fields = x.split(splitter);(fields(0),fields(1).toInt)}
//对推荐结果集中的用户与菜品编码进行反规约操作
val law_userlist2= law_userlist1.map(x =>(x._2,x._1)).collect.toMap
val law_urllist2= law_urllist1.map(x =>(x._2,x._1)).collect.toMap
//加载训练数据
val trainDataPath = “G:\python和智能推荐实战\智能推荐发学生部分\部分数据2\分割的数据\trainRatings”
val trainData = sc.textFile(trainDataPath).map{
x =>val fields = x.slice(1,x.size-1).split(splitter);(fields(1).toInt,fields(2).toInt)}
//聚合
val trainUserRated = trainData.combineByKey((x:Int) => List(x),(c:List[Int],x:Int) =>x::c,(c1:List[Int],c2:List[Int]) => c1:::c2).cache()
//加载推荐模型
val modelPath = “G:\python和智能推荐实战\智能推荐发学生部分\部分数据2\存储的模型”
import org.apache.spark.rdd.RDD
val dataModel:RDD[(Int,List[(Int)])] = sc.objectFile(Int,List[(Int)])
//过滤训练数据中已有的菜品,生成可推荐的新菜品集合
val dataModelNew = dataModel.join(trainUserRated).map(x =>(x._1,(x._2._1.diff(x._2._2))))
val k = 30
//为用户((推荐10份菜品(UserNo,MealNo)
val recommendation = dataModelNew.map(x =>(x._1,x._2.take(k))).flatMap(x=>x._2.map(y =>(x._1,y)))
//反编码后的推荐结果集
val recommendationRecords = recommendation.map{case(user,url) => (law_userlist2.get(user).get,law_urllist2.get(url).get)}
recommendationRecords.take(4)
recommendationRecords.repartition(1).saveAsTextFile(“G:\python和智能推荐实战\智能推荐发学生部分\部分数据2\推荐结果”)
推荐结果部分数据如下表4-1-1
表4-1-1
用户ID 推荐网址
1885938045 [1]“http://www..cn/info/hunyin/jichengfagui/201402122880224.html"
[2]"http://www..cn/info/hunyin/jichengfagui/201402122880224.html”
[3]“http://www.*.cn/info/minfa/minfafagui/2011060261777.htmll”
521041235.1 [1]“http://www..cn/info/laodong/ldzyjygl/20140312142058.html"
[2]"http://www..cn/info/laodong/ldzyjydt/201405273016968.html”
[3]“http://www..cn/info/laodong/ldxw/20140312142057.html"
[4]"http://www..cn/info/laodong/ldzyjygl/20140312142060.html”
[5]“http://www.*.cn/info/laodong/ldxw/20140312142057.html”
1794522633 [1]“http://www..cn/info/laodong/yuangongguanli/2010122086395.html"
[2]"http://www..cn/info/laodong/yuangongguanli/2010122086395.html”
[3]“http://www..cn/info/laodong/ldzyhtzy/2010122387376.html"
[4]"http://www..cn/info/laodong/ldzyhtzy/2010122387376.html”
[5]“http://www.*.cn/info/laodong/ldzyhtzy/2010122387376.html”
5项目总结及展望
本次项目从案例背景实现目标系统,整体架构及流程设计等展开分步骤较完整的实现系统。推荐系统研究是一个新兴的研究领域,在电子商务环境下发展应用很快,已取得了很好的研究成果。但是研究也存在很多问题:在用户信息收集上主要依赖用户的显式评价,自动获得用户的隐式信息方面做得不够;过分集中对协同过滤推荐方面的研究,同时对稀疏问题及冷开始问题等经典问题缺乏有效的解决方法;对推荐系统的开发与应用,尤其是与其他其他系统的集成应用研究不够;推荐技术应用集中在网上购物上,没有把推荐技术运用到其他行业等等。未来推荐系统研究应该从着眼于技术转向更多的关注用户。
6 附录
关键源代码
数据集分割关键代码:代码清单 3-2-3 数据分割为训练集、验证集、测试集
// 分割点:编码数据: timestamp,user,url,urlType
val RatingCodeList = sc.textFile(inputpath,6).map{ x => val fields = x.slice(2,x.size-2).split(“,”);(fields(0).toLong,fields(1).toInt,fields(2).toInt,fields(3).toInt)}.sortBy(_._1)
//添加一列1…开始的数字
val zipRatingCodeList = RatingCodeList .zipWithIndex.mapValues(x =>(x+1))
//定义数据分割点
val totalNum = RatingCodeList.count()
val splitPoint1 = totalNum0.8 toInt
val splitPoint2 = totalNum0.9 toInt
//生成训练集数据
val train = zipRatingCodeList.filter(x =>(x._2<splitPoint1)).map(x =>(x._1._1,x._1._2,x._1._3,x._1._4))
//生成验证集数据
val validate = zipRatingCodeList.filter(x =>(x._2>=splitPoint1 && x._2<splitPoint2)).map(x =>(x._1._1,x._1._2,x._1._3,x._1._4))
//生成测试集数据
val test = zipRatingCodeList.filter(x =>(x._2>=splitPoint2)).map(x =>(x._1._1,x._1._2,x._1._3,x._1._4))
建立模型关键代码:代码 3-3 Spark 基于用户的协同过滤算法实现
//3.3模型构建
import scala.math._
// 应用Jaccard 公式求相似度
val userSimilarityPre = userSimilarityBase.map(data => {
val user1=data._1._1
val user2= data._1._2
val similarity = (min(data._2._1, data._2._3))*1.0/(data._2._2 + data._2._4)
((user1, user2), similarity)
}).combineByKey(
x=>x,
(x:Double,y:Double)=>(x+y),
(x:Double,y:Double)=>(x+y))
// 用户相似度 (user,(user,similarity))
val userSimilarity = userSimilarityPre.map(x=>((x._1._2,x._1._1),x._2)).union(userSimilarityPre).
map(x=>(x._1._1,(x._1._2,x._2)))
// 初始化推荐集合(user,List(item,similarity))
val statistics = trainDataFiltered.join(userSimilarity).map(x=>(x._2._2._1,(x._2._1,x._2._2._2))).combineByKey(
(x:(Int,Double)) => List(x),
(c:List[(Int,Double)], x:(Int,Double)) => c :+ x ,
(c1:List[(Int,Double)], c2:List[(Int,Double)]) => c1 ::: c2)
//生成推荐集合(user,List(item))
//为每个user,截取前recommendItemNum个item记录
val dataModel = statistics.
map(data=>{val key = data._1; val value = data._2.sortWith(_._2>_._2);
if(value.size>recommendItemNum){
(key,value.slice(0,recommendItemNum))
}else{(key,value)}}).
map(x=>(x._1,x._2.map(x=>x._1)))
实现推荐结果关键代码:代码 4-1-1 实现推荐结果
//对推荐结果集中的用户与菜品编码进行反规约操作
val law_userlist2= law_userlist1.map(x =>(x._2,x._1)).collect.toMap
val law_urllist2= law_urllist1.map(x =>(x._2,x._1)).collect.toMap
//过滤训练数据中已有的菜品,生成可推荐的新菜品集合
val dataModelNew = dataModel.join(trainUserRated).map(x =>(x._1,(x._2._1.diff(x._2._2))))
val k = 30
//为用户((推荐10份菜品(UserNo,MealNo)
val recommendation = dataModelNew.map(x =>(x._1,x._2.take(k))).flatMap(x=>x._2.map(y =>(x._1,y)))
//反编码后的推荐结果集
val recommendationRecords = recommendation.map{case(user,url) => (law_userlist2.get(user).get,law_urllist2.get(url).get)}
参考文献
[0]Spark大数据技术与应用 主编:肖芳,张良均 .
[1]周军锋,汤显,郭景峰.一种优化的协同过滤推荐算法[J].计算机研究与发展.
[2]邓爱林,朱扬勇,施伯乐.基于项目评分预测的协同过滤推荐算法[J].软件学报.
[3]林鸿飞,杨志豪,赵晶.基于内容和合作模式的信息推荐机制[J].中文信息学报.
[4]崔林,宋瀚涛,陆玉昌.基于语义相似性的资源协同过滤技术研究[J].北京理工大学学报.文章来源:https://www.toymoban.com/news/detail-496503.html
文章来源地址https://www.toymoban.com/news/detail-496503.html
到了这里,关于spark法律服务大数据智能推荐(自己动手做的,完整过程+源码)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!