【推荐系统】电影推荐系统(二)
文章目錄
- 前言
- 一、ALS算法簡介
- 二、ALS代碼示例
- 2.1、模型訓(xùn)練
- 2.2、電影相似計算
前言
本文主要闡述如何將電影評價矩陣通過ALS算法計算出電影特征,請大家參考。
一、ALS算法簡介
??????ALS是交替最小二乘法的簡稱,是2008年以來,用的比較多的協(xié)同過濾算法。它已經(jīng)集成到Spark的Mllib庫中,使用起來比較方便。
??????這里可以想象一下,每個人的性格愛好可以認為是一個抽象的模型,每個人的模型都有自己的一個特點。因此,每個人對于商品的評價都有自己的一套規(guī)律,ALS算法就是可以通過這些已有的評價數(shù)據(jù)中,盡可能的計算出一個誤差較小的模型,來預(yù)測對未評價商品的評分。
??????ALS算法的核心就是假設(shè):打分矩陣是近似低跌的,通過用戶u成行,商品v成列的方式,形成一個u * v的打分矩陣A。由于用戶和商品的數(shù)量非常多,所以形成的矩陣A也非常龐大,這樣在處理起來比較困難,所以矩陣A可以通過兩個小矩陣的乘積來表示,如下圖所示:
??????實際情況中k遠小于u和v的值, 一般在50-200之,具體取值是什么不重要,因為這個只是為了最后計算出誤差最小的模型。這樣一來得到一個用戶矩陣和商品矩陣。通過兩個矩陣的乘積得到矩陣A。
??????ALS的交替二字用的很是精辟,就是指需要先隨機生成一個矩陣U0,然后求另一個矩陣V0。然后固定V0,求U1。這樣交替下去,誤差會逐漸變小。另外ALS不能保證會收斂到全局最優(yōu)解。但是在實際情況中,ALS最后是不是最優(yōu)解影響并不是很大。因為,本來就沒有最好的推薦,只有更好的推薦。
交替最小二乘法百科
二、ALS代碼示例
首先需要一批用戶電影評價的數(shù)據(jù),數(shù)據(jù)格式如下:
#用戶id,#電影id,#評分,#時間戳
1,31,2.5,1260759144
1,1029,3.0,1260759179
數(shù)據(jù)用逗號隔開。
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)建一個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()// 隨機切分數(shù)據(jù)集,生成訓(xùn)練集和測試集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)計算val result = for( rank <- Array(50); lambda <- Array( 0.01, 0.1, 1 ))yield {val model = ALS.train(trainData, rank, 5, lambda)// 計算當前參數(shù)對應(yīng)模型的rmse,返回Doubleval rmse = getRMSE( model, testData )( rank, lambda, rmse )}// 控制臺打印輸出最優(yōu)參數(shù)println(">>>"+result.minBy(_._3))}def getRMSE(model: MatrixFactorizationModel, data: RDD[Rating]): Double = {// 計算預(yù)測評分val userProducts = data.map(item => (item.user, item.product))val predictRating = model.predict(userProducts)// 以uid,mid作為外鍵,inner join實際觀測值和預(yù)測值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開始,先加載評分數(shù)據(jù),這里是加載的全部,實際情況中要看情況,如果新聞類的推薦,可能只需要加載最近一段時間的數(shù)據(jù)即可。
- 將數(shù)據(jù)拆分為預(yù)測集和測試集。ALS算法本來就是一個預(yù)測的算法,如果全部用來預(yù)測計算的話,將無法知道預(yù)測的結(jié)果誤差有多少,拆分兩個集合這個由于上學(xué)考試時的AB卷,通過A卷平時練習(xí),B卷檢測真實的成績。
- adjustALSParam方法進行模型的計算,rank是特征維度,本地計算時只取了50,生成的時候應(yīng)該多取一些值分別測試。lambda是為了防止過擬合。
- ALS.train方法中迭代次數(shù),只填寫了5次,這個數(shù)值肯定是越大越精確,當然也會越耗費時間。
- 最后通過getRMSE方法計算誤差。打印出誤差最小的一組數(shù)據(jù),即可在線上使用。
2.2、電影相似計算
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)建一個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))}// 對所有電影兩兩計算它們的相似度,先做笛卡爾積val movieRecs = movieFeatures.cartesian(movieFeatures).filter{// 把自己跟自己的配對過濾掉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) // 過濾出相似度大于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() )} }- 在模型計算時,計算出特征值50和lambda為0.1時誤差最小,這里就直接使用。
- 之前有說als算法會將一個大的矩陣分解為用戶特征矩陣和物品特征矩陣。所以,這里可以通過model.productFeatures直接獲取物品特征矩陣。最后通過余弦相似度計算出兩兩電影的相似度。
- 生成環(huán)境有時物品的數(shù)量太多,比如電商中的商品。由于數(shù)量太多,在特征計算時如果選用全部的商品計算是一種不明智的選擇。因此可以通過先聚類再計算相似度。比如我們單純的需要做手機推薦時,那么這個時候計算一個手機和一件衣服的相似度是沒有意義的。
最后會得到一批這樣的數(shù)據(jù)。每一行第一個是電影id,后面跟上一個數(shù)組,數(shù)組中每一項的第一個是電影id,第二個是相似度。
電影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ù)可以在每一個電影的詳細資料之后附加一個為你推薦的欄位,將這個電影最相似的幾個電影查詢出來。例如騰訊視頻。
總結(jié)
以上是生活随笔為你收集整理的【推荐系统】电影推荐系统(二)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux—任务计划
- 下一篇: windows虚拟显示器开发(二)WDD