《智能推薦技術(shù)與應(yīng)用》課程作品(項(xiàng)目)報(bào)告
水院的同學(xué)不要抄襲呀!
1 作品(項(xiàng)目)目標(biāo)
與搜索引擎不同,推薦系統(tǒng)并不需要用戶提供明確的需求,而是通過(guò)分析用戶的歷史行為,主動(dòng)為用戶推薦能夠滿足他們興趣和需求的信息。為了能夠更好地滿足用戶需求,需要依據(jù)其網(wǎng)站的海量數(shù)據(jù),研究用戶的興趣偏好,分析用戶的需求和行為,發(fā)現(xiàn)用戶的興趣點(diǎn),從而引導(dǎo)用戶發(fā)現(xiàn)自己的信息需求,將長(zhǎng)尾網(wǎng)頁(yè)(長(zhǎng)尾網(wǎng)頁(yè)是指網(wǎng)頁(yè)的點(diǎn)擊情況滿足長(zhǎng)尾理論中尾巴部分的網(wǎng)頁(yè))準(zhǔn)確地推薦給所需用戶,即使用推薦引擎來(lái)為用戶提供個(gè)性化的專(zhuān)業(yè)服務(wù)。
目標(biāo):
1.按地域研究用戶訪問(wèn)時(shí)間、訪問(wèn)內(nèi)容、訪問(wèn)次數(shù)等分析主題,深入了解用戶訪問(wèn)網(wǎng)站的行為、目的及關(guān)心的內(nèi)容(主要指統(tǒng)計(jì)信息)。
2.借助大量用戶訪問(wèn)記錄,使用推薦算法發(fā)現(xiàn)用戶訪問(wèn)習(xí)慣,對(duì)不同用戶推薦相關(guān)服務(wù)頁(yè)面。
2 作品(項(xiàng)目)方案設(shè)計(jì)
2.1.1對(duì)用戶的數(shù)據(jù)分析
屬性名稱(chēng) 屬性說(shuō)明 屬性名稱(chēng) 屬性說(shuō)明
userID 用戶ID pagePath 路徑
timestamp 時(shí)間戳 FullReferrerURL 入口網(wǎng)址
2.1.2根據(jù)推薦算法進(jìn)行建模評(píng)測(cè)
協(xié)同過(guò)濾推薦( Collaborative Filtering Recommendation )技術(shù)是推薦系統(tǒng)中應(yīng)用最早和最為成功的技術(shù)之一。它一般采用最近鄰技術(shù),利用用戶的歷史喜好信息計(jì)算用戶之間的距離,然后利用目標(biāo)用戶的最近鄰居用戶對(duì)商品評(píng)價(jià)的加權(quán)評(píng)價(jià)值來(lái)預(yù)測(cè)目標(biāo)用戶對(duì)特定商品的喜好程度,系統(tǒng)從而根據(jù)這一喜好程度來(lái)對(duì) 目標(biāo)用戶進(jìn)行推薦。協(xié)同過(guò)濾推薦的最大優(yōu)點(diǎn)是對(duì)推薦對(duì)象沒(méi)有特殊的要求,能處理非結(jié)構(gòu)化的復(fù)雜對(duì)象,如音樂(lè)、電影。
2.1.3根據(jù)模型得出推薦結(jié)果。
當(dāng)經(jīng)過(guò)評(píng)估,選定推薦模型后,則可以應(yīng)用模型對(duì)所有用戶生成推薦數(shù)據(jù)。
3 智能推薦建模及模型評(píng)測(cè)
3.1 數(shù)據(jù)探索
源數(shù)據(jù)表結(jié)構(gòu)及說(shuō)明如表3-1:
表 31 網(wǎng)站日志數(shù)據(jù)屬性及其說(shuō)明
屬性名稱(chēng) 屬性說(shuō)明 屬性名稱(chēng) 屬性說(shuō)明
realIP 真實(shí)ip fullURLID 網(wǎng)址類(lèi)型
realAreacode 地區(qū)編號(hào) hostname 源地址名
userAgent 瀏覽器代理 pageTitle 網(wǎng)頁(yè)標(biāo)題
userOS 用戶瀏覽器類(lèi)型 pageTitleCategoryId 標(biāo)題類(lèi)型ID
userID 用戶ID pageTitleCategoryName 標(biāo)題類(lèi)型名稱(chēng)
clientID 客戶端ID pageTitleKw 標(biāo)題類(lèi)型關(guān)鍵字
timestamp 時(shí)間戳 fullReferrrer 入口源
timestamp_format 標(biāo)準(zhǔn)化時(shí)間 FullReferrerURL 入口網(wǎng)址
pagePath 路徑 organicKeyword 搜索關(guān)鍵字
ymd 年月日 source 搜索源
fullURL 網(wǎng)址
3.1.0 數(shù)據(jù)來(lái)源
從已有的數(shù)據(jù)讀取,然后轉(zhuǎn)為臨時(shí)表。
代碼3-1-0
//讀取數(shù)據(jù)
val data = spark.read.csv(“G:\python和智能推薦實(shí)戰(zhàn)\智能推薦發(fā)學(xué)生部分\lawdata_20140819_20141015.csv”)
//轉(zhuǎn)為臨時(shí)表
data.registerTempTable(“l(fā)aw”)
3.1.1 網(wǎng)頁(yè)類(lèi)型統(tǒng)計(jì)
統(tǒng)計(jì)內(nèi)容為:網(wǎng)頁(yè)類(lèi)型、記錄數(shù)及其所占總記錄百分比。
代碼 3-1-1 按網(wǎng)頁(yè)類(lèi)型統(tǒng)計(jì)
----統(tǒng)計(jì)網(wǎng)頁(yè)類(lèi)型
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()
運(yùn)行結(jié)果如表 3-1-1-1所示,從中發(fā)現(xiàn)點(diǎn)擊與咨詢相關(guān)的網(wǎng)頁(yè)(網(wǎng)頁(yè)類(lèi)型為101,http://www..cn/ask/)的記錄的占比為49.15%,其次是知識(shí)相關(guān)網(wǎng)頁(yè)(網(wǎng)頁(yè)類(lèi)型為107,http://www..com/info/)占比約為21.84%,剩余其他的類(lèi)型網(wǎng)頁(yè)(網(wǎng)頁(yè)類(lèi)型為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 咨詢類(lèi)別內(nèi)部統(tǒng)計(jì)
統(tǒng)計(jì)內(nèi)容為:101網(wǎng)頁(yè)類(lèi)型的子類(lèi)型、記錄數(shù)及其所占101網(wǎng)頁(yè)類(lèi)型總記錄百分比。
代碼 3-1-2 咨詢類(lèi)內(nèi)部統(tǒng)計(jì)
----咨詢類(lèi)別內(nèi)部統(tǒng)計(jì)
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()
其結(jié)果如表3-1-2-1所示。其中瀏覽咨詢內(nèi)容頁(yè)(101003)記錄最多,其次是咨詢列表頁(yè)(101002)和咨詢首頁(yè)(101001)。結(jié)合上述初步結(jié)論,可以得出用戶都喜歡通過(guò)瀏覽問(wèn)題的方式找到自己需要的信息,而不是以提問(wèn)的方式或者查看長(zhǎng)篇的知識(shí)的方式。
表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 網(wǎng)頁(yè)中帶有“?”記錄統(tǒng)計(jì)
統(tǒng)計(jì)所有訪問(wèn)網(wǎng)頁(yè)中帶有“?”的總記錄數(shù)。統(tǒng)計(jì)分析訪問(wèn)網(wǎng)頁(yè)中帶有?的所有記錄中,各網(wǎng)頁(yè)類(lèi)型、記錄數(shù)、占訪問(wèn)網(wǎng)頁(yè)中帶有?的記錄總數(shù)的百分比。
代碼 3-1-3 網(wǎng)頁(yè)中帶有“?”記錄統(tǒng)計(jì)及各個(gè)類(lèi)別占比
-----統(tǒng)計(jì)visiturl中帶有?的所有記錄。
spark.sql(“select count() as num from law where _c10 like ‘%?%’ ").show()
65492
-----統(tǒng)計(jì)帶有?的所有記錄中,各網(wǎng)頁(yè)類(lèi)型所占比例
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
其結(jié)果整理見(jiàn)表3-1-3-1。包含“?”總記錄數(shù)為65492,特別在其他網(wǎng)頁(yè)這一類(lèi)型中占了98%左右,比重較大,因此需要進(jìn)一步分析該類(lèi)型網(wǎng)頁(yè)的內(nèi)部規(guī)律,但在知識(shí)相關(guān)與法規(guī)專(zhuān)題中的占比僅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 數(shù)據(jù)預(yù)處理(異常值、缺失值處理,編碼,數(shù)據(jù)集分割等)
3.2.1 篩選數(shù)據(jù)
把無(wú).html點(diǎn)擊行為,中間類(lèi)型網(wǎng)頁(yè)(帶midques_關(guān)鍵字),網(wǎng)址中帶有?的記錄,重復(fù)數(shù)據(jù),法律服務(wù)以外的數(shù)據(jù)過(guò)濾掉。
代碼 3-2-1 篩選數(shù)據(jù)
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%’ ")
根據(jù)分析目標(biāo)以及探索結(jié)果可知咨詢、知識(shí)、法規(guī)專(zhuān)題是其主要業(yè)務(wù)來(lái)源,故需篩選咨詢、知識(shí)與法規(guī)專(zhuān)題相關(guān)的記錄,將此部分?jǐn)?shù)據(jù)作為模型分析需要的數(shù)據(jù)。
3.2.2 數(shù)據(jù)變換
在3.2.1的基礎(chǔ)上繼續(xù)篩選,規(guī)則如下:
1.去掉訪問(wèn)網(wǎng)址中包含browse.html或browse_的記錄。
2.如果訪問(wèn)網(wǎng)址中包含ask/question_關(guān)鍵字且ask/question_與.html之間的字符串全為數(shù)字,保留;否則ask/question_與.html之間的字符串不全為數(shù)字,舍棄。
代碼 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 ‘%法律快車(chē)%’
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$’ ")
//將數(shù)據(jù)轉(zhuǎn)為rdd
val b = a.rdd
//保存數(shù)據(jù)
b.repartition(1).saveAsTextFile(“G:\python和智能推薦實(shí)戰(zhàn)\智能推薦發(fā)學(xué)生部分\部分?jǐn)?shù)據(jù)2\cleandata”)
結(jié)果如下: b.take(1) //(時(shí)間戳,用戶ID,url,url類(lèi)型)
Array([1422860000000,268392030.1,http://www.*.cn/info/shuifa/slb/2012111978933.html,107001]
3.2.3 數(shù)據(jù)編碼
編碼思路為:1)求得原始用戶以及URL的去重值,并按照ASCII值進(jìn)行排序;2)使用排序后的原始用戶以及URL的下標(biāo)值來(lái)代替該用戶或URL;3)使用編碼后的值替換原始數(shù)據(jù)中的值。使用Spark對(duì)原始數(shù)據(jù)進(jìn)行上述編碼處理,其代碼如3-3-1所示。
代碼清單 3-3-1 基于3.2.2的數(shù)據(jù)編碼及替換
//加載數(shù)據(jù), 原始數(shù)據(jù)
val dataAll = sc.textFile(“G:\python和智能推薦實(shí)戰(zhàn)\智能推薦發(fā)學(xué)生部分\部分?jǐn)?shù)據(jù)2\cleandata”).map{x => val fields=x.split(“,”); (fields(0),fields(1),fields(2),fields(3))}
// 排序去重后用戶、URL數(shù)據(jù)
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)
// 構(gòu)造用戶、URL編碼
val allUserIdList = allUserList.zipWithIndex.map(data=>(data._1,data._2.toInt))
val allUrlIdList = allUrlList.zipWithIndex.map(data=>(data._1,data._2.toInt))
// 保存編碼數(shù)據(jù)
allUserIdList.map(x => x._1 +“,”+x._2).repartition(1).saveAsTextFile(“G:/python和智能推薦實(shí)戰(zhàn)/智能推薦發(fā)學(xué)生部分/部分?jǐn)?shù)據(jù)2/law_userlist”)
allUrlIdList.map(x => x._1 +“,”+x._2).repartition(1).saveAsTextFile(“G:/python和智能推薦實(shí)戰(zhàn)/智能推薦發(fā)學(xué)生部分/部分?jǐn)?shù)據(jù)2/law_urllist”)
// 替換原始數(shù)據(jù)
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))
// 保存編碼后數(shù)據(jù)
replacedDataAll.saveAsTextFile(“G:/python和智能推薦實(shí)戰(zhàn)/智能推薦發(fā)學(xué)生部分/部分?jǐn)?shù)據(jù)2/law_data_replaced”)
//數(shù)據(jù)格式:時(shí)間戳,用戶編碼,url編碼,url類(lèi)型
3.2.3 數(shù)據(jù)集分割
經(jīng)過(guò)預(yù)處理后的數(shù)據(jù),再次分為知識(shí)類(lèi)、咨詢類(lèi)和法規(guī)類(lèi)數(shù)據(jù),針對(duì)每類(lèi)數(shù)據(jù)都采用統(tǒng)一的處理方式。
數(shù)據(jù)首先按照時(shí)間戳分為3份,分別是:訓(xùn)練集、驗(yàn)證集和測(cè)試集,對(duì)應(yīng)占比為80%、10%、10%。其分割代碼如代碼清單 419所示。
代碼清單 3-2-3 數(shù)據(jù)分割為訓(xùn)練集、驗(yàn)證集、測(cè)試集
// 分割點(diǎn):編碼數(shù)據(jù): timestamp,user,url,urlType
val inputpath = “G:\python和智能推薦實(shí)戰(zhàn)\智能推薦發(fā)學(xué)生部分\部分?jǐn)?shù)據(jù)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…開(kāi)始的數(shù)字
val zipRatingCodeList = RatingCodeList .zipWithIndex.mapValues(x =>(x+1))
//定義數(shù)據(jù)分割點(diǎn)
val totalNum = RatingCodeList.count()
val splitPoint1 = totalNum0.8 toInt
val splitPoint2 = totalNum0.9 toInt
//生成訓(xùn)練集數(shù)據(jù)
val train = zipRatingCodeList.filter(x =>(x._2<splitPoint1)).map(x =>(x._1._1,x._1._2,x._1._3,x._1._4))
//生成驗(yàn)證集數(shù)據(jù)
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))
//生成測(cè)試集數(shù)據(jù)
val test = zipRatingCodeList.filter(x =>(x._2>=splitPoint2)).map(x =>(x._1._1,x._1._2,x._1._3,x._1._4))
//訓(xùn)練集數(shù)據(jù)總數(shù)
train.count
//驗(yàn)證集數(shù)據(jù)總數(shù)
validate.count
//測(cè)試集數(shù)據(jù)總數(shù)
test.count
//存儲(chǔ)訓(xùn)練集,驗(yàn)證集,測(cè)試集
train.repartition(1).saveAsTextFile(“G:/python和智能推薦實(shí)戰(zhàn)/智能推薦發(fā)學(xué)生部分/部分?jǐn)?shù)據(jù)2/分割的數(shù)據(jù)/trainRatings”)
validate.repartition(1).saveAsTextFile(“G:/python和智能推薦實(shí)戰(zhàn)/智能推薦發(fā)學(xué)生部分/部分?jǐn)?shù)據(jù)2/分割的數(shù)據(jù)/validateRatings”)
test.repartition(1).saveAsTextFile(“G:/python和智能推薦實(shí)戰(zhàn)/智能推薦發(fā)學(xué)生部分/部分?jǐn)?shù)據(jù)2/分割的數(shù)據(jù)/testRatings”)
訓(xùn)練集用于訓(xùn)練模型,驗(yàn)證集用于評(píng)估模型以找到最優(yōu)模型,測(cè)試集對(duì)最優(yōu)模型進(jìn)行驗(yàn)證。
3.3 建立基于用戶的協(xié)同過(guò)濾模型
基于用戶的協(xié)同過(guò)濾,即通過(guò)不同用戶對(duì)項(xiàng)目的評(píng)分來(lái)評(píng)測(cè)用戶之間的相似性,搜索目標(biāo)用戶的最近鄰,然后根據(jù)最近鄰的評(píng)分向目標(biāo)用戶產(chǎn)生推薦。具體描述如下:
1.計(jì)算相似度
用戶之間的相似度通過(guò)每個(gè)用戶對(duì)項(xiàng)目的評(píng)分向量(注意,在本文中如果用戶對(duì)某個(gè)URL進(jìn)行訪問(wèn),那么該項(xiàng)目就為1,如果沒(méi)有訪問(wèn),那么就為0)計(jì)算得到。相似度的計(jì)算可以使用任何向量相似度計(jì)算公式,但在實(shí)際使用中,需要選擇一種契合模型數(shù)據(jù)的算法。同時(shí),如果現(xiàn)有相似度計(jì)算算法不符合實(shí)際情況,也可對(duì)其加以改進(jìn)。
2.尋找與目標(biāo)用戶最近鄰的K個(gè)用戶
在計(jì)算出各個(gè)用戶之間的相似度后,可以找到所有與目標(biāo)用戶的相似度大于某一閾值的近鄰用戶(第一步粗略過(guò)濾),然后對(duì)這些用戶按照相似度進(jìn)行排序,得到前K個(gè)近鄰用戶。
3.通過(guò)這K個(gè)用戶進(jìn)行推薦
得到K個(gè)近鄰用戶后,怎么推薦呢?當(dāng)然,這里的方式有多種。比如使用相似度和所有K個(gè)用戶的項(xiàng)目對(duì)應(yīng)加權(quán)進(jìn)行推薦。
根據(jù)上述算法原理,編寫(xiě)Spark的基于用戶的協(xié)同過(guò)濾算法,其代碼如代碼清單 417所示。
代碼 3-3 Spark 基于用戶的協(xié)同過(guò)濾算法實(shí)現(xiàn)
//3.3模型構(gòu)建
import scala.math._
// 處理參數(shù)
val trainDataPath = “G:\python和智能推薦實(shí)戰(zhàn)\智能推薦發(fā)學(xué)生部分\部分?jǐn)?shù)據(jù)2\分割的數(shù)據(jù)\trainRatings”
val modelPath = “G:\python和智能推薦實(shí)戰(zhàn)\智能推薦發(fā)學(xué)生部分\部分?jǐn)?shù)據(jù)2\存儲(chǔ)的模型”
val minItemsPerUser = 2
val recommendItemNum = 10
val splitter = “,”
// 加載訓(xùn)練集數(shù)據(jù),
val trainDataRaw= sc.textFile(trainDataPath).map{x=>val fields=x.slice(1,x.size-1).split(splitter); (fields(1).toInt,fields(2).toInt)}
// 獲取訓(xùn)練集數(shù)據(jù),以單用戶最小訪問(wèn)Item數(shù)過(guò)濾
val trainDataFiltered = trainDataRaw.groupBy(_._1).filter(data=>data._2.toList.size>=minItemsPerUser).flatMap(_._2)
// (user,item)pair 的重復(fù)次數(shù)統(tǒng)計(jì)
val trainUserItemNumPre = trainDataFiltered.countByValue().toArray.map(x=>(x._1._1,(x._1._2,x._2.toInt)))
// user的訪問(wèn)次數(shù)統(tǒng)計(jì)
val trainUserNumPre = trainDataFiltered.keys.countByValue().toArray
// 轉(zhuǎn)化為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)))
// 應(yīng)用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))
//為每個(gè)user,截取前recommendItemNum個(gè)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)))
// 存儲(chǔ)模型
dataModel.repartition(1).saveAsObjectFile(modelPath)
println("Model saved")
sc.stop()
模型結(jié)果: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 模型評(píng)測(cè)(選取常用評(píng)測(cè)指標(biāo)實(shí)現(xiàn))
好的推薦系統(tǒng)能夠滿足用戶的需求,推薦其感興趣但不全是熱門(mén)的物品,同時(shí)也需要用戶反饋意見(jiàn)幫助完善其推薦系統(tǒng)。因此,好的推薦系統(tǒng)不僅能預(yù)測(cè)用戶的行為,而且能幫助用戶發(fā)現(xiàn)可能會(huì)感興趣,但卻不易被發(fā)現(xiàn)的物品。同時(shí),推薦系統(tǒng)還應(yīng)該幫助商家將長(zhǎng)尾中的好商品發(fā)掘出來(lái),推薦給可能會(huì)對(duì)它們感興趣的用戶。在實(shí)際應(yīng)用中,評(píng)測(cè)推薦系統(tǒng)是必不可少的。評(píng)測(cè)指標(biāo)主要來(lái)源于如下3種評(píng)測(cè)推薦效果的實(shí)驗(yàn)方法,即離線測(cè)試、用戶調(diào)查和在線實(shí)驗(yàn)。
離線測(cè)試是通過(guò)從實(shí)際系統(tǒng)中提取數(shù)據(jù)集,然后采用各種推薦算法對(duì)其進(jìn)行測(cè)試,獲各個(gè)算法的評(píng)測(cè)指標(biāo)。這種實(shí)驗(yàn)方法的好處是不需要真實(shí)用戶參與。
基于用戶的協(xié)同過(guò)濾推薦模型評(píng)測(cè),加載推薦模型與測(cè)試數(shù)據(jù)集,為每個(gè)用戶選取k個(gè)推薦結(jié)果,與測(cè)試數(shù)據(jù)集的實(shí)際記錄進(jìn)行匹配。具體實(shí)現(xiàn)過(guò)程如代碼如3-4所示:
代碼 3-4 模型評(píng)測(cè)
//模型評(píng)測(cè)
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
/**
- 評(píng)估基于用戶的協(xié)同過(guò)濾模型,需要輸入以下參數(shù)
- testDataPath: 測(cè)試數(shù)據(jù)(userid,itemid)
- modelPath:模型存儲(chǔ)目錄
- minRatedNumPerUser:單用戶評(píng)價(jià)菜品的最小次數(shù)
- kList:推薦數(shù)量K值列表
- resultPath:評(píng)估結(jié)果的存儲(chǔ)目錄
- splitter:輸入原始數(shù)據(jù)分隔符
*/
// 匹配輸入?yún)?shù)
val testDataPath = “G:\python和智能推薦實(shí)戰(zhàn)\智能推薦發(fā)學(xué)生部分\部分?jǐn)?shù)據(jù)2\分割的數(shù)據(jù)\testRatings”
val modelPath = “G:\python和智能推薦實(shí)戰(zhàn)\智能推薦發(fā)學(xué)生部分\部分?jǐn)?shù)據(jù)2\存儲(chǔ)的模型”
val minRatedNumPerUser = 2
val kList = “30,50,70,90,110”.split(“,”).map(_.toInt)
val resultPath = “G:\python和智能推薦實(shí)戰(zhàn)\智能推薦發(fā)學(xué)生部分\部分?jǐn)?shù)據(jù)2\評(píng)估結(jié)果1”
val splitter = “,”
// 加載推薦模型,為每個(gè)user抽取前K(最大值)個(gè)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()
// 加載測(cè)試集記錄
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() )
// 過(guò)濾測(cè)試集記錄 (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與測(cè)試集的item,進(jìn)行匹配比較
// 計(jì)算不同的K值下的recall, precision值
val results = for ( k <- kList ;
// 與測(cè)試集比較,獲得matched記錄數(shù):
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 )
//存儲(chǔ)結(jié)果
sc.parallelize(results.map(x => x._1 + "," + x._2 + "," + x._3+ "," + x._4+ "," + x._5)).repartition(1).saveAsTextFile(resultPath)
println("Evaluation completed.")
sc.stop()
在另外一些電子商務(wù)網(wǎng)站中,用戶只有二元選擇,比如喜歡和不喜歡,瀏覽與否等。針對(duì)這類(lèi)型的數(shù)據(jù)預(yù)測(cè),就要用分類(lèi)準(zhǔn)確度,其中的評(píng)測(cè)指標(biāo)有準(zhǔn)確率(P),它表示用戶對(duì)一個(gè)被推薦產(chǎn)品感興趣對(duì)的可能性。召回率?表示一個(gè)用戶喜歡的產(chǎn)品被推薦的概率。F1指標(biāo)綜合考慮了準(zhǔn)確率與召回率因素,能更好地評(píng)價(jià)算法的優(yōu)劣(F1越大,說(shuō)明算法越優(yōu))。
評(píng)測(cè)結(jié)果如下:
模型 模型參數(shù) k值 召回率(R) 精確率§ 綜合指標(biāo)F1
基于用戶的協(xié)同過(guò)濾算法模型 單用戶評(píng)價(jià)過(guò)的最小物品數(shù)=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 實(shí)現(xiàn)推薦結(jié)果
4.1.1 對(duì)所有用戶進(jìn)行法律服務(wù)網(wǎng)址推薦
當(dāng)經(jīng)過(guò)評(píng)估,選定推薦模型后,則可以應(yīng)用模型對(duì)所有用戶生成推薦數(shù)據(jù)。
代碼 4-1-1 實(shí)現(xiàn)推薦結(jié)果
//設(shè)置編碼數(shù)據(jù)集的路徑
val law_userlist= “G:\python和智能推薦實(shí)戰(zhàn)\智能推薦發(fā)學(xué)生部分\部分?jǐn)?shù)據(jù)2\law_userlist”
val law_urllist= “G:\python和智能推薦實(shí)戰(zhàn)\智能推薦發(fā)學(xué)生部分\部分?jǐn)?shù)據(jù)2\law_urllist”
//加載用戶編碼數(shù)據(jù)集,菜品編碼數(shù)據(jù)集
val splitter = “,”
“G:\python和智能推薦實(shí)戰(zhàn)\智能推薦發(fā)學(xué)生部分\部分?jǐn)?shù)據(jù)2\law_userlist\part-00000”
“G:\python和智能推薦實(shí)戰(zhàn)\智能推薦發(fā)學(xué)生部分\部分?jǐn)?shù)據(jù)2\law_urllist\part-00000”
val law_userlist1= sc.textFile(“G:\python和智能推薦實(shí)戰(zhàn)\智能推薦發(fā)學(xué)生部分\部分?jǐn)?shù)據(jù)2\law_userlist\part-00000”).map{x =>val fields = x.split(splitter);(fields(0),fields(1).toInt)}
val law_urllist1= sc.textFile(“G:\python和智能推薦實(shí)戰(zhàn)\智能推薦發(fā)學(xué)生部分\部分?jǐn)?shù)據(jù)2\law_urllist\part-00000”).map{x => val fields = x.split(splitter);(fields(0),fields(1).toInt)}
//對(duì)推薦結(jié)果集中的用戶與菜品編碼進(jìn)行反規(guī)約操作
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
//加載訓(xùn)練數(shù)據(jù)
val trainDataPath = “G:\python和智能推薦實(shí)戰(zhàn)\智能推薦發(fā)學(xué)生部分\部分?jǐn)?shù)據(jù)2\分割的數(shù)據(jù)\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和智能推薦實(shí)戰(zhàn)\智能推薦發(fā)學(xué)生部分\部分?jǐn)?shù)據(jù)2\存儲(chǔ)的模型”
import org.apache.spark.rdd.RDD
val dataModel:RDD[(Int,List[(Int)])] = sc.objectFile(Int,List[(Int)])
//過(guò)濾訓(xùn)練數(shù)據(jù)中已有的菜品,生成可推薦的新菜品集合
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)))
//反編碼后的推薦結(jié)果集
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和智能推薦實(shí)戰(zhàn)\智能推薦發(fā)學(xué)生部分\部分?jǐn)?shù)據(jù)2\推薦結(jié)果”)
推薦結(jié)果部分?jǐn)?shù)據(jù)如下表4-1-1
表4-1-1
用戶ID 推薦網(wǎng)址
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項(xiàng)目總結(jié)及展望
本次項(xiàng)目從案例背景實(shí)現(xiàn)目標(biāo)系統(tǒng),整體架構(gòu)及流程設(shè)計(jì)等展開(kāi)分步驟較完整的實(shí)現(xiàn)系統(tǒng)。推薦系統(tǒng)研究是一個(gè)新興的研究領(lǐng)域,在電子商務(wù)環(huán)境下發(fā)展應(yīng)用很快,已取得了很好的研究成果。但是研究也存在很多問(wèn)題:在用戶信息收集上主要依賴(lài)用戶的顯式評(píng)價(jià),自動(dòng)獲得用戶的隱式信息方面做得不夠;過(guò)分集中對(duì)協(xié)同過(guò)濾推薦方面的研究,同時(shí)對(duì)稀疏問(wèn)題及冷開(kāi)始問(wèn)題等經(jīng)典問(wèn)題缺乏有效的解決方法;對(duì)推薦系統(tǒng)的開(kāi)發(fā)與應(yīng)用,尤其是與其他其他系統(tǒng)的集成應(yīng)用研究不夠;推薦技術(shù)應(yīng)用集中在網(wǎng)上購(gòu)物上,沒(méi)有把推薦技術(shù)運(yùn)用到其他行業(yè)等等。未來(lái)推薦系統(tǒng)研究應(yīng)該從著眼于技術(shù)轉(zhuǎn)向更多的關(guān)注用戶。
6 附錄
關(guān)鍵源代碼
數(shù)據(jù)集分割關(guān)鍵代碼:代碼清單 3-2-3 數(shù)據(jù)分割為訓(xùn)練集、驗(yàn)證集、測(cè)試集
// 分割點(diǎn):編碼數(shù)據(jù): 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…開(kāi)始的數(shù)字
val zipRatingCodeList = RatingCodeList .zipWithIndex.mapValues(x =>(x+1))
//定義數(shù)據(jù)分割點(diǎn)
val totalNum = RatingCodeList.count()
val splitPoint1 = totalNum0.8 toInt
val splitPoint2 = totalNum0.9 toInt
//生成訓(xùn)練集數(shù)據(jù)
val train = zipRatingCodeList.filter(x =>(x._2<splitPoint1)).map(x =>(x._1._1,x._1._2,x._1._3,x._1._4))
//生成驗(yàn)證集數(shù)據(jù)
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))
//生成測(cè)試集數(shù)據(jù)
val test = zipRatingCodeList.filter(x =>(x._2>=splitPoint2)).map(x =>(x._1._1,x._1._2,x._1._3,x._1._4))
建立模型關(guān)鍵代碼:代碼 3-3 Spark 基于用戶的協(xié)同過(guò)濾算法實(shí)現(xiàn)
//3.3模型構(gòu)建
import scala.math._
// 應(yīng)用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))
//為每個(gè)user,截取前recommendItemNum個(gè)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)))
實(shí)現(xiàn)推薦結(jié)果關(guān)鍵代碼:代碼 4-1-1 實(shí)現(xiàn)推薦結(jié)果
//對(duì)推薦結(jié)果集中的用戶與菜品編碼進(jìn)行反規(guī)約操作
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
//過(guò)濾訓(xùn)練數(shù)據(jù)中已有的菜品,生成可推薦的新菜品集合
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)))
//反編碼后的推薦結(jié)果集
val recommendationRecords = recommendation.map{case(user,url) => (law_userlist2.get(user).get,law_urllist2.get(url).get)}
參考文獻(xiàn)
[0]Spark大數(shù)據(jù)技術(shù)與應(yīng)用 主編:肖芳,張良均 .
[1]周軍鋒,湯顯,郭景峰.一種優(yōu)化的協(xié)同過(guò)濾推薦算法[J].計(jì)算機(jī)研究與發(fā)展.
[2]鄧愛(ài)林,朱揚(yáng)勇,施伯樂(lè).基于項(xiàng)目評(píng)分預(yù)測(cè)的協(xié)同過(guò)濾推薦算法[J].軟件學(xué)報(bào).
[3]林鴻飛,楊志豪,趙晶.基于內(nèi)容和合作模式的信息推薦機(jī)制[J].中文信息學(xué)報(bào).
[4]崔林,宋瀚濤,陸玉昌.基于語(yǔ)義相似性的資源協(xié)同過(guò)濾技術(shù)研究[J].北京理工大學(xué)學(xué)報(bào).文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-496503.html
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-496503.html
到了這里,關(guān)于spark法律服務(wù)大數(shù)據(jù)智能推薦(自己動(dòng)手做的,完整過(guò)程+源碼)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!