【推荐系统】电影推荐系统(二)
文章目錄
- 前言
- 一、ALS算法簡(jiǎn)介
- 二、ALS代碼示例
- 2.1、模型訓(xùn)練
- 2.2、電影相似計(jì)算
前言
本文主要闡述如何將電影評(píng)價(jià)矩陣通過(guò)ALS算法計(jì)算出電影特征,請(qǐng)大家參考。
一、ALS算法簡(jiǎn)介
??????ALS是交替最小二乘法的簡(jiǎn)稱(chēng),是2008年以來(lái),用的比較多的協(xié)同過(guò)濾算法。它已經(jīng)集成到Spark的Mllib庫(kù)中,使用起來(lái)比較方便。
??????這里可以想象一下,每個(gè)人的性格愛(ài)好可以認(rèn)為是一個(gè)抽象的模型,每個(gè)人的模型都有自己的一個(gè)特點(diǎn)。因此,每個(gè)人對(duì)于商品的評(píng)價(jià)都有自己的一套規(guī)律,ALS算法就是可以通過(guò)這些已有的評(píng)價(jià)數(shù)據(jù)中,盡可能的計(jì)算出一個(gè)誤差較小的模型,來(lái)預(yù)測(cè)對(duì)未評(píng)價(jià)商品的評(píng)分。
??????ALS算法的核心就是假設(shè):打分矩陣是近似低跌的,通過(guò)用戶(hù)u成行,商品v成列的方式,形成一個(gè)u * v的打分矩陣A。由于用戶(hù)和商品的數(shù)量非常多,所以形成的矩陣A也非常龐大,這樣在處理起來(lái)比較困難,所以矩陣A可以通過(guò)兩個(gè)小矩陣的乘積來(lái)表示,如下圖所示:
??????實(shí)際情況中k遠(yuǎn)小于u和v的值, 一般在50-200之,具體取值是什么不重要,因?yàn)檫@個(gè)只是為了最后計(jì)算出誤差最小的模型。這樣一來(lái)得到一個(gè)用戶(hù)矩陣和商品矩陣。通過(guò)兩個(gè)矩陣的乘積得到矩陣A。
??????ALS的交替二字用的很是精辟,就是指需要先隨機(jī)生成一個(gè)矩陣U0,然后求另一個(gè)矩陣V0。然后固定V0,求U1。這樣交替下去,誤差會(huì)逐漸變小。另外ALS不能保證會(huì)收斂到全局最優(yōu)解。但是在實(shí)際情況中,ALS最后是不是最優(yōu)解影響并不是很大。因?yàn)?#xff0c;本來(lái)就沒(méi)有最好的推薦,只有更好的推薦。
交替最小二乘法百科
二、ALS代碼示例
首先需要一批用戶(hù)電影評(píng)價(jià)的數(shù)據(jù),數(shù)據(jù)格式如下:
#用戶(hù)id,#電影id,#評(píng)分,#時(shí)間戳
1,31,2.5,1260759144
1,1029,3.0,1260759179
數(shù)據(jù)用逗號(hào)隔開(kāi)。
2.1、模型訓(xùn)練
import breeze.numerics.sqrt import org.apache.spark.SparkConf import org.apache.spark.mllib.recommendation.{ALS, MatrixFactorizationModel, Rating} import org.apache.spark.rdd.RDD import org.apache.spark.sql.SparkSessionobject ALSTrainerTest {def main(args: Array[String]): Unit = {val config = Map("spark.cores" -> "local[*]")val sparkConf = new SparkConf().setMaster(config("spark.cores")).setAppName("OfflineRecommender")// 創(chuàng)建一個(gè)SparkSessionval spark = SparkSession.builder().config(sparkConf).getOrCreate()import spark.implicits._//加載數(shù)據(jù)val ratingRDD = spark.read.textFile("E:\\ratings.csv").map(_.split(",")).rdd.map( rating => Rating( rating(0).toInt,rating(1).toInt,rating(2).toDouble ) ).cache()// 隨機(jī)切分?jǐn)?shù)據(jù)集,生成訓(xùn)練集和測(cè)試集val splits = ratingRDD.randomSplit(Array(0.8, 0.2))val trainingRDD = splits(0)val testRDD = splits(1)// 模型參數(shù)選擇,輸出最優(yōu)參數(shù)adjustALSParam(trainingRDD, testRDD)spark.close()}def adjustALSParam(trainData: RDD[Rating], testData: RDD[Rating]): Unit ={// 50, 100, 200, 300//循環(huán)計(jì)算val result = for( rank <- Array(50); lambda <- Array( 0.01, 0.1, 1 ))yield {val model = ALS.train(trainData, rank, 5, lambda)// 計(jì)算當(dāng)前參數(shù)對(duì)應(yīng)模型的rmse,返回Doubleval rmse = getRMSE( model, testData )( rank, lambda, rmse )}// 控制臺(tái)打印輸出最優(yōu)參數(shù)println(">>>"+result.minBy(_._3))}def getRMSE(model: MatrixFactorizationModel, data: RDD[Rating]): Double = {// 計(jì)算預(yù)測(cè)評(píng)分val userProducts = data.map(item => (item.user, item.product))val predictRating = model.predict(userProducts)// 以u(píng)id,mid作為外鍵,inner join實(shí)際觀測(cè)值和預(yù)測(cè)值val observed = data.map( item => ( (item.user, item.product), item.rating ) )val predict = predictRating.map( item => ( (item.user, item.product), item.rating ) )// 內(nèi)連接得到(uid, mid),(actual, predict)sqrt(observed.join(predict).map{case ( (uid, mid), (actual, pre) ) =>val err = actual - preerr * err}.mean())} }- 從入口main開(kāi)始,先加載評(píng)分?jǐn)?shù)據(jù),這里是加載的全部,實(shí)際情況中要看情況,如果新聞?lì)惖耐扑],可能只需要加載最近一段時(shí)間的數(shù)據(jù)即可。
- 將數(shù)據(jù)拆分為預(yù)測(cè)集和測(cè)試集。ALS算法本來(lái)就是一個(gè)預(yù)測(cè)的算法,如果全部用來(lái)預(yù)測(cè)計(jì)算的話,將無(wú)法知道預(yù)測(cè)的結(jié)果誤差有多少,拆分兩個(gè)集合這個(gè)由于上學(xué)考試時(shí)的AB卷,通過(guò)A卷平時(shí)練習(xí),B卷檢測(cè)真實(shí)的成績(jī)。
- adjustALSParam方法進(jìn)行模型的計(jì)算,rank是特征維度,本地計(jì)算時(shí)只取了50,生成的時(shí)候應(yīng)該多取一些值分別測(cè)試。lambda是為了防止過(guò)擬合。
- ALS.train方法中迭代次數(shù),只填寫(xiě)了5次,這個(gè)數(shù)值肯定是越大越精確,當(dāng)然也會(huì)越耗費(fèi)時(shí)間。
- 最后通過(guò)getRMSE方法計(jì)算誤差。打印出誤差最小的一組數(shù)據(jù),即可在線上使用。
2.2、電影相似計(jì)算
import org.apache.spark.SparkConf import org.apache.spark.mllib.recommendation.{ALS, MatrixFactorizationModel, Rating} import org.apache.spark.sql.SparkSession import org.jblas.DoubleMatrixobject ALSTrainerTest2 {def main(args: Array[String]): Unit = {val config = Map("spark.cores" -> "local[*]")val sparkConf = new SparkConf().setMaster(config("spark.cores")).setAppName("OfflineRecommender")// 創(chuàng)建一個(gè)SparkSessionval spark = SparkSession.builder().config(sparkConf).getOrCreate()import spark.implicits._val ratingRDD = spark.read.textFile("E:\\webapps\\github\\MovieRecommendSystem\\recommender\\DataLoader\\src\\main\\resources\\ratings.csv").map(_.split(",")).rdd.map( rating => Rating( rating(0).toInt,rating(1).toInt,rating(2).toDouble ) ).cache()val model = ALS.train(ratingRDD, 50, 5, 0.1)val movieFeatures = model.productFeatures.map{case (mid, features) => (mid, new DoubleMatrix(features))}// 對(duì)所有電影兩兩計(jì)算它們的相似度,先做笛卡爾積val movieRecs = movieFeatures.cartesian(movieFeatures).filter{// 把自己跟自己的配對(duì)過(guò)濾掉case (a, b) => a._1 != b._1}.map{case (a, b) => {val simScore = this.consinSim(a._2, b._2)( a._1, ( b._1, simScore ) )}}.filter(_._2._2 > 0.6) // 過(guò)濾出相似度大于0.6的.groupByKey().map{case (mid, items) => MovieRecs( mid, items.toList.sortWith(_._2 > _._2).map(x => Recommendation(x._1, x._2)) )}.toDF()print(">>>"+movieRecs.show(10,false))spark.close()}// 求向量余弦相似度def consinSim(movie1: DoubleMatrix, movie2: DoubleMatrix):Double ={movie1.dot(movie2) / ( movie1.norm2() * movie2.norm2() )} }- 在模型計(jì)算時(shí),計(jì)算出特征值50和lambda為0.1時(shí)誤差最小,這里就直接使用。
- 之前有說(shuō)als算法會(huì)將一個(gè)大的矩陣分解為用戶(hù)特征矩陣和物品特征矩陣。所以,這里可以通過(guò)model.productFeatures直接獲取物品特征矩陣。最后通過(guò)余弦相似度計(jì)算出兩兩電影的相似度。
- 生成環(huán)境有時(shí)物品的數(shù)量太多,比如電商中的商品。由于數(shù)量太多,在特征計(jì)算時(shí)如果選用全部的商品計(jì)算是一種不明智的選擇。因此可以通過(guò)先聚類(lèi)再計(jì)算相似度。比如我們單純的需要做手機(jī)推薦時(shí),那么這個(gè)時(shí)候計(jì)算一個(gè)手機(jī)和一件衣服的相似度是沒(méi)有意義的。
最后會(huì)得到一批這樣的數(shù)據(jù)。每一行第一個(gè)是電影id,后面跟上一個(gè)數(shù)組,數(shù)組中每一項(xiàng)的第一個(gè)是電影id,第二個(gè)是相似度。
電影id|[[電影id,相似度]]
1216 |[[268,0.9020642201372278], [100553,0.8698425508833494]]
1792 |[[108188,0.900184780661061], [100487,0.8869996818771411]
2688 |[[27808,0.8402992760950369], [122,0.8381670349448246]]
最后這些數(shù)據(jù)可以在每一個(gè)電影的詳細(xì)資料之后附加一個(gè)為你推薦的欄位,將這個(gè)電影最相似的幾個(gè)電影查詢(xún)出來(lái)。例如騰訊視頻。
總結(jié)
以上是生活随笔為你收集整理的【推荐系统】电影推荐系统(二)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Linux—任务计划
- 下一篇: windows虚拟显示器开发(二)WDD