1. 程式人生 > >spark 隱含因子音樂推薦

spark 隱含因子音樂推薦

2017-07-22

音樂推薦

本示例基於spark高階程式設計. 實現了一個對

隱因子推薦演算法( Latent Factor Analysis)

用使用者和產品之間的互動, 來找到潛在的分類, 並對使用者進行推薦.

隱含語義模型LFM和LSI,LDA,Topic Model 都屬於隱含語義分析技術,是一類概念,他們在本質上是相通的,都是找出潛在的主題或分類。和該技術相關演算法有pLSA(probabilitistic Latent Semantic Analysis)概率隱語義主題模型 、LDA(Latent Dirichlet Allocation)文件主題生成模型、隱含類別模型(latent class model )、隱含主題模型(latent topic model )、矩陣分解(matrix factorization )。這些技術和方法在本質上是相通的,其中很多方法都可以用於個性化推薦系統。 隱含語義分析核心思想是通過隱含特徵(latent factor)聯絡使用者興趣和物品。 基於興趣分類的方法大概需要解決3個問題。  如何給物品進行分類?  如何確定使用者對哪些類的物品感興趣,以及感興趣的程度?  對於一個給定的類,選擇哪些屬於這個類的物品推薦給使用者,以及如何確定這些物品一個類中的權重? 對分類來說,可能因人而異,很難到位。很難控制分類的粒度;很難給一個物品多個分類;很難給出多維度的分類(分類是可以有很多維度的,比如按照作者分類、按照來源分類、按照地域分類);很難決定一個物品在某一個分類中的權重。 能否從資料出發,自動地找到那些類,進行個性化推薦?隱含語義分析技術即用於解決上述問題。 隱含語義分析技術的分類來自對使用者行為的統計,代表了使用者對物品分類的看法。隱含語義分析技術和ItemCF在物品分類方面的思想類似,如果兩個物品被很多使用者同時喜歡,那麼這兩個物品就很有可能屬於同一個類。

矩陣分解原理

如下面的公式所示.R是m,n階矩陣.P是m,k階矩陣. Q是k,n階矩陣. 其中, k就是隱藏的類別屬性.

第二個公式表示R的第i行等於分界後矩陣P的第i行和矩陣Q相乘.

矩陣分解演算法有時稱為矩陣補全(matrix completion)演算法,因為原始矩陣R 可能非常稀疏,但乘積$PQ$ 是稠密的. 因此模型只是對R的一種近似。

對具體例項, 我們假設P為m個使用者,具有k種興趣(每個值代表了對應模型的一個隱含特徵). Q為n個物品,具有k種分類.$PQ$的乘積即為”使用者_特徵”表和”物品_特徵”表的乘積, 結果為全部使用者對全部物品的興趣.

R是一個使用者物品行為的稀疏矩陣, 它只是所有使用者物品關係的一個樣本. 可以看成一個對基本事實的觀察. 用k個特徵,卻可以很好的歸納總結這些基本事實.根據這些歸納, 可以對還沒有發生的使用者物品的行為進行補全. 補全的值即可用於推薦.

$PQ$應該趨近R, 但不應該完全和R一致. 將R矩陣分解成$PQ$並不容易.完全一致的分解可能也會產生過擬合. 因為偶然的行為等噪音也會計算在內. 直接同時得到$PQ$的最優解是不可能的. 但如果P已知,求Q的最優解是非常容易的,反之亦然。 所以我們可以通過迭代的方式逐步趨近$PQ$ 如使用交替最小二乘(Alternating Least Squares,ALS)演算法, 來獲得$PQ$近似值.

雖然Q是未知的,但我們可以把它初始化為隨機行向量矩陣。接著運用簡單的線性代數,就能在給定R和Q的條件下求出P的最優解。實際上,P的第i行是R 的第i行和Q的函式,因此可以很容易分開計算P的每一行。因為P的每一行可以分開計算,所以我們可以將其並行化,而並行化是大規模計算的一大優點。

交替最小二乘推薦演算法(Alternating Least Squares,ALS)

實際的目標是最小化$|Q^T(QQ^T)^{-1} - P_i|$,或者最小化 兩個矩陣的平方誤差。這就是演算法名稱中“最小二乘”的來由。這裡給出方程式只是為 了說明行向量計算方法,但實踐中從來不會對矩陣求逆,會藉助於 QR分解 之類的方法,這種方法速度更快而且更直接。 可以由P計算每個$Q_i$。然後又可以由Q計算P,這樣反覆下去。這就是演算法名稱中“交替”的來由。Q剛開始是隨機的. P和Q最終會收斂得到一個合適的結果。

將ALS 演算法用於隱性資料矩陣分解時,ALS 矩陣分解要稍微複雜一點兒。它不是直接分解輸入矩陣R,而是分解由0 和1 組成的矩陣R1,當R中元素為正時,R1中對應元素為1,否則為0。R中的具體值後面會以權重的形式反映出來.

準備資料

示例使用Audioscrobbler公開發布的一個數據集。Audioscrobbler 是last.fm 的第一個音樂推薦系統。Audioscrobbler 資料集有些特別,因為它只記錄了播放資料. 下載地址 裡面有幾個檔案,主要的資料集在檔案user_artist_data.txt 中,它由3列組成:( userid artistid playcount)包含141 000 個使用者和160萬個藝術家,記錄了約2420 萬條使用者播放藝術家歌曲的資訊,其中包括播放次數資訊。artist_data.txt 檔案由2列組成(artistid artist_name)中給出了每個藝術家的ID和對應的名字。artist_alias.txt 檔案由2列組成(badid, goodid)儲存藝術家別稱.

測試

$$ scala scala> val rawUserArtistData = sc.textFile(“/Users/zhouhh/Downloads/profiledata_06-May-2005/user_artist_data.txt”) rawUserArtistData: org.apache.spark.rdd.RDD[String] = /Users/zhouhh/Downloads/profiledata_06-May-2005/user_artist_data.txt MapPartitionsRDD[1] at textFile at :24 scala> rawUserArtistData.take(10) res0: Array[String] = Array(1000002 1 55, 1000002 1000006 33, 1000002 1000007 8, 1000002 1000009 144, 1000002 1000010 314, 1000002 1000013 8, 1000002 1000014 42, 1000002 1000017 69, 1000002 1000024 329, 1000002 1000025 1)

scala> case class UserArtist(userid:Int, artistid:Int, playcount:Int)

scala> val ua = rawUserArtistData.map(_.split(“ “)).map{x=> | UserArtist(x(0).toInt,x(1).toInt,x(2).toInt)} ua: org.apache.spark.rdd.RDD[UserArtist] = MapPartitionsRDD[4] at map at :28

scala> ua.take(10) res5: Array[UserArtist] = Array(UserArtist(1000002,1,55), UserArtist(1000002,1000006,33), UserArtist(1000002,1000007,8), UserArtist(1000002,1000009,144), UserArtist(1000002,1000010,314), UserArtist(1000002,1000013,8), UserArtist(1000002,1000014,42), UserArtist(1000002,1000017,69), UserArtist(1000002,1000024,329), UserArtist(1000002,1000025,1)) scala> ua.map(_.userid).stats() res8: org.apache.spark.util.StatCounter = (count: 24296858, mean: 1947573.265353, stdev: 496000.544975, max: 2443548.000000, min: 90.000000)

scala> import org.apache.spark.mllib.recommendation._ import org.apache.spark.mllib.recommendation._

scala> val trainData = rawUserArtistData.map(_.split(“ “)).map{x=> | Rating(x(0).toInt,x(1).toInt,x(2).toInt)}.cache() trainData: org.apache.spark.rdd.RDD[org.apache.spark.mllib.recommendation.Rating] = MapPartitionsRDD[9] at map at :29 scala> val model = ALS.trainImplicit(trainData, 10, 5, 0.01, 1.0) Caused by: java.lang.OutOfMemoryError: Java heap space $$

切換伺服器,提高記憶體 $$

[[email protected] ~]$ spark-shell –master local[4] –driver-memory 6g

scala> import org.apache.spark.mllib._ import org.apache.spark.mllib._

scala> import org.apache.spark.mllib.recommendation._ import org.apache.spark.mllib.recommendation._ scala> val rawUserArtistData = sc.textFile(“/home/zhouhh/nlp/data/profiledata/user_artist_data.txt”) rawUserArtistData: org.apache.spark.rdd.RDD[String] = /home/zhouhh/nlp/data/profiledata/user_artist_data.txt MapPartitionsRDD[1] at textFile at :24 scala> val trainData = rawUserArtistData.map(_.split(" ")).map{x=> | Rating(x(0).toInt,x(1).toInt,x(2).toInt)}.cache() trainData: org.apache.spark.rdd.RDD[org.apache.spark.mllib.recommendation.Rating] = MapPartitionsRDD[3] at map at :32 $$

訓練模型

$$

scala> val model = ALS.trainImplicit(trainData, 10, 5, 0.01, 1.0) 17/07/05 15:36:05 WARN BLAS: Failed to load implementation from: com.github.fommil.netlib.NativeSystemBLAS 17/07/05 15:36:05 WARN BLAS: Failed to load implementation from: com.github.fommil.netlib.NativeRefBLAS 17/07/05 15:36:06 WARN LAPACK: Failed to load implementation from: com.github.fommil.netlib.NativeSystemLAPACK 17/07/05 15:36:06 WARN LAPACK: Failed to load implementation from: com.github.fommil.netlib.NativeRefLAPACK model: org.apache.spark.mllib.recommendation.MatrixFactorizationModel = [email protected]c8859b4 scala> model.userFeatures.mapValues(_.mkString(“, “)).first() res1: (Int, String) = (90,0.9103583693504333, -0.799676775932312, -0.7170624732971191, 0.27575400471687317, 0.23960699141025543, 0.21480463445186615, -0.12654761970043182, 0.33688923716545105, 0.31023073196411133, -0.2494485229253769) $$

ALS.trainImplicit() 的引數包括以下幾個。

  • rank = 10 模型的潛在因素的個數,即“使用者-特徵”和“產品-特徵”矩陣的列數;一般來說,它也是矩陣的階。
  • iterations = 5 矩陣分解迭代的次數;迭代的次數越多,花費的時間越長,但分解的結果可能會更好。
  • lambda = 0.01 標準的過擬合引數;值越大越不容易產生過擬合,但值太大會降低分解的準確度。
  • alpha = 1.0 控制矩陣分解時,被觀察到的“使用者-產品”互動相對沒被觀察到的互動的權重。

推薦

給使用者2093760做推薦,5位藝術家. $$

scala> val recommendations = model.recommendProducts(2093760, 5) recommendations: Array[org.apache.spark.mllib.recommendation.Rating] = Array(Rating(2093760,1300642,0.031684423392518514), Rating(2093760,2814,0.03133836778736454), Rating(2093760,1001819,0.03098141120876122), Rating(2093760,1007614,0.030441838331559903), Rating(2093760,4605,0.030321088541257573))

$$

rating第三個值,對這類ALS 演算法,它是一個在0 到1 之間的模糊值, 值越大,推薦質量越好。它不是概率,但可以把它理解成對0/1 值的一個估計,0 表示用 戶不喜歡播放藝術家的歌曲,1 表示喜歡播放藝術家的歌曲。

推薦評價

一個指標是觀測者操作特性(Receiver Operating Characteristic,ROC,http://en.wikipedia.org/wiki/Receiver_operating_characteristic)曲線。上一段中的指標等於ROC曲線下區域的面積,稱為AUC(Area Under the Curve)。 可以把AUC 看成是隨機選擇的好推薦比隨機選擇的差推薦的排名高的概率。

參考

《Spark高階資料分析》 《推薦系統設計》

如非註明轉載, 均為原創. 本站遵循知識共享CC協議,轉載請註明來源