ALS矩陣分解推薦模型

ALS矩陣分解推薦模型

其實通過模型來預測一個user對一個item的評分,思想類似線性迴歸做預測,大致如下

定義一個預測模型(數學公式),

然後確定一個損失函數,

將已有數據作爲訓練集,

不斷迭代來最小化損失函數的值,

最終確定參數,把參數套到預測模型中做預測。

 

矩陣分解的預測模型是:


損失函數是:


我們就是要最小化損失函數,從而求得參數q和p。

 

矩陣分解模型的物理意義


我們希望學習到一個P代表user的特徵,Q代表item的特徵。特徵的每一個維度代表一個隱性因子,比如對電影來說,這些隱性因子可能是導演,演員等。當然,這些隱性因子是機器學習到的,具體是什麼含義我們不確定。

學習到P和Q之後,我們就可以直接P乘以Q就可以預測所有user對item的評分了。



講完矩陣分解推薦模型,下面到als了(全稱Alternatingleast squares)。其實als就是上面損失函數最小化的一個求解方法,當然還有其他方法比如SGD等。

als論文中的損失函數是(跟上面那個稍微有點不同)


每次迭代,

         固定M,逐個更新每個user的特徵u(對u求偏導,令偏導爲0求解)。

         固定U,逐個更新每個item的特徵m(對m求偏導,令偏導爲0求解)。

論文中是這樣推導的


這是每次迭代求u的公式。求m的類似。

 

爲了更清晰的理解,這裏結合spark的als代碼講解。

spark源碼中實現als有三個版本,一個是LocalALS.scala(沒有用spark),一個是SparkALS.scala(用了spark做並行優化),一個是mllib中的ALS。

 

本來LocalALS.scala和SparkALS.scala這個兩個實現是官方爲了開發者學習使用spark展示的,

mllib中的ALS可以用於實際的推薦。

但是mllib中的ALS做了很多優化,不適合初學者研究來理解als算法。

因此,下面我拿LocalALS.scala和SparkALS.scala來講解als算法。

 

LocalALS.scala

    // Iteratively update movies then users
    for (iter <- 1 to ITERATIONS) {
      println(s"Iteration $iter:")
      ms = (0 until M).map(i => updateMovie(i, ms(i), us, R)).toArray  //固定用戶,逐個更新所有電影的特徵
      us = (0 until U).map(j => updateUser(j, us(j), ms, R)).toArray   //固定電影,逐個更新所有用戶的特徵
      println("RMSE = " + rmse(R, ms, us))
      println()
    }

  //更新第j個user的特徵向量
  def updateUser(j: Int, u: RealVector, ms: Array[RealVector], R: RealMatrix) : RealVector = {
    var XtX: RealMatrix = new Array2DRowRealMatrix(F, F) //F是隱性因子的數量
    var Xty: RealVector = new ArrayRealVector(F)
    // For each movie that the user rated 遍歷該user評分過的movie.顯然,這裏默認該用戶評分過所有電影,所以是0-M.實際應用求解,只需要遍歷該用戶評分過的電影.
    for (i <- 0 until M) {
      val m = ms(i)
      // Add m * m^t to XtX 外積後 累加到XtX
      XtX = XtX.add(m.outerProduct(m)) //向量與向量的外積:一個當作列向量,一個當作行向量,做矩陣乘法,結果是一個矩陣
      // Add m * rating to Xty
      Xty = Xty.add(m.mapMultiply(R.getEntry(i, j)))
    }
    // Add regularization coefficients to diagonal terms
    for (d <- 0 until F) {
      XtX.addToEntry(d, d, LAMBDA * M)
    }
    // Solve it with Cholesky 其實是解一個A*x=b的方程
    new CholeskyDecomposition(XtX).getSolver.solve(Xty)
  }

再結合論文中的公式


其實代碼中的XtX就是公式中左邊紅圈的部分,Xty就是右邊紅圈的部分。

同理,更新每個電影的特徵m類似,這裏不再重複。

SparkALS.scala

    for (iter <- 1 to ITERATIONS) {
      println(s"Iteration $iter:")
      ms = sc.parallelize(0 until M, slices)
                .map(i => update(i, msb.value(i), usb.value, Rc.value))
                .collect()
      msb = sc.broadcast(ms) // Re-broadcast ms because it was updated
      us = sc.parallelize(0 until U, slices)
                .map(i => update(i, usb.value(i), msb.value, Rc.value.transpose()))
                .collect()
      usb = sc.broadcast(us) // Re-broadcast us because it was updated
      println("RMSE = " + rmse(R, ms, us))
      println()
    }

SparkALS版本相對於LocalALS的亮點時,做了並行優化。LocalALS中,每個user的特徵是串行更新的。而SparkALS中,是並行更新的。

 

 

參考資料:

《Large-scale Parallel Collaborative Filtering for the Netflix Prize》(als-wr原論文)

《Matrix Factorization Techniques for Recommender Systems》(矩陣分解模型的好材料)

https://github.com/apache/spark/blob/master/examples/src/main/scala/org/apache/spark/examples/LocalALS.scala

https://github.com/apache/spark/blob/master/examples/src/main/scala/org/apache/spark/examples/SparkALS.scala




本文作者:linger

本文鏈接:http://blog.csdn.net/lingerlanlan/article/details/44085913



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章