ALS(Alternating Least Squares)是一種廣泛使用的推薦系統演算法,特別用於協同過濾(Collaborative Filtering)任務。在 Apache Spark 中,ALS 被實現為
org.apache.spark.ml.recommendation.ALS
類,適用於大規模資料集,並能夠有效地處理稀疏矩陣,常用於推薦引擎。
- ALS 演算法的基本思想
- 在Spark 中使用 ALS
- ALS實踐練習
ALS 演算法的基本思想
ALS 的主要思想是:
-
分解矩陣:ALS 透過將使用者-專案評分矩陣分解為兩個低秩矩陣——使用者特徵矩陣和專案特徵矩陣。這使得可以透過使用者和專案之間的特徵相似度來預測評分。
-
交替最佳化:該演算法交替地固定使用者特徵矩陣和專案特徵矩陣,並透過最小化損失函式(通常是均方誤差)來最佳化這兩個矩陣。具體的最佳化步驟是:
- 在固定專案特徵矩陣的情況下,最佳化使用者特徵矩陣。
- 然後在固定使用者特徵矩陣的情況下,最佳化專案特徵矩陣。
- 重複以上步驟直到收斂。
在Spark 中使用 ALS
在 Spark 中,ALS 提供了一種簡單而高效的方式來構建推薦模型。以下是使用 Spark 中的 ALS 的基本步驟:
-
匯入必要的庫:
import org.apache.spark.ml.recommendation.ALS import org.apache.spark.sql.SparkSession
-
建立 Spark 會話:
val spark = SparkSession.builder() .appName("ALSExample") .getOrCreate()
-
準備資料:
資料應包含使用者 ID、專案 ID 和評分,通常以 DataFrame 格式儲存。
val ratings = Seq( (0, 0, 4), (0, 1, 2), (1, 0, 5), (1, 1, 1) ).toDF("userId", "itemId", "rating")
-
構建 ALS 模型:
val als = new ALS() .setUserCol("userId") .setItemCol("itemId") .setRatingCol("rating") .setColdStartStrategy("drop") // 保證對待沒有評分的預測結果的處理 .setRank(10) // 設定特徵向量的維度 .setMaxIter(10) // 最大迭代次數 val model = als.fit(ratings)
-
生成推薦:
可以使用模型生成使用者和專案的推薦。
val userRecs = model.recommendForAllUsers(5) // 為所有使用者推薦 5 個專案 val itemRecs = model.recommendForAllItems(5) // 為所有專案推薦 5 個使用者
ALS實踐練習
資料展示:
三列資料分別為使用者 ID、物品 ID 和評分
1,1,5.0
1,2,1.0
1,3,5.0
1,4,1.0
2,1,5.0
2,2,1.0
2,3,5.0
2,4,1.0
3,1,1.0
3,2,5.0
3,3,1.0
3,4,5.0
4,1,1.0
4,2,5.0
4,3,1.0
4,4,5.0
在hdfs檔案系統,建立目錄,名為mymllib5,並將linux上的myrating檔案資料,上傳到hdfs
hadoop fs -mkdir /mymllib5
hadoop fs -put /myrating /mymllib5
啟動spark-shell
spark-shell
為了執行協同過濾操作,需要首先匯入執行統計所依賴的包
import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
import org.apache.spark.mllib.recommendation.ALS
import org.apache.spark.mllib.recommendation.MatrixFactorizationModel
import org.apache.spark.mllib.recommendation.Rating
讀取hdfs上的myrating文字檔案
val data = sc.textFile("hdfs://192.168.88.161:8020/mymllib5/myrating")
把資料轉化成rating型別,即[Int, Int, Double]的RDD;
val ratings = data.map(_.split(",") match {
case Array(user, item, rate) =>
Rating(user.toInt, item.toInt, rate.toDouble)
})
檢查一下資料格式是否符合要求
ratings.foreach{x => println(x)}
劃分訓練集和測試集,比例分別是0.8和0.2。
val splits = ratings.randomSplit(Array(0.8, 0.2))
將80%作為訓練資料集
val training = splits(0)
將20%作為測試資料集
val test = splits(1)
指定引數值,然後使用ALS訓練資料建立推薦模型:
val rank = 10
val numIterations = 10
val model = ALS.train(training, rank, numIterations, 0.01)
使用訓練好的推薦模型對使用者商品進行預測評分,得到預測評分的資料集
val testUsersProducts = test.map { case Rating(user, product, rate) => (user, product) }
使用訓練好的推薦模型對使用者商品進行預測評分,得到預測評分的資料集:
val predictions =
model.predict(testUsersProducts).map { case Rating(user, product, rate) =>((user, product), rate)
}
將真實評分資料集與預測評分資料集進行合併。這裡,Join操作類似於SQL的inner join操作,返回結果是前面和後面集合中配對成功的,過濾掉關聯不上的。
val ratesAndPreds = test.map { case Rating(user, product, rate) => ((user, product), rate) }.join(predictions)
我們把結果輸出,對比一下真實結果與預測結果:
ratesAndPreds.foreach(println)
檢視輸出效果:
比如,第一條結果記錄((1,4),(1.0,2.0765385175171436))中,(1,4)分別表示1號使用者和4號商品,而1.0是實際的估計分值,2.0765385175171436是經過推薦的預測分值。
當然,我們也可以針對於某一個人進行預測,對比結果。
val result = model.recommendProducts(2, 1)
result.foreach(println)
然後計算均方差,這裡的r1就是真實結果,r2就是預測結果:
val MSE = ratesAndPreds.map { case ((user, product), (r1, r2)) =>
val err = (r1 - r2)
err * err
}.mean()
把輸出結果列印出來:
println("Mean Squared Error = " + MSE)
輸出效果:
看到打分的均方差值為1.44左右