ALS音樂推薦(上)
本篇文章的開頭筆者提出一個疑問,何為數據科學,數據科學是做什麽的?大家帶著這個疑問去讀接下來的這篇音樂推薦的公眾號。
從經驗上講,推薦引擎屬於大規模機器學習,在日常購物中大家或許深有體會,比如:你在淘寶上瀏覽了一些商品,或者購買了一些商品,那麽淘寶就會根據你的偏好給你推薦一些其他類似的商品。然而,相比較其他機器學習算法,推薦引擎的輸出更加的直觀,有時候的推薦效果讓人吃驚。作為機器學習開篇文章,本篇文章會系統的介紹基於Audioscrobbler數據集的音樂推薦。
數據集介紹
Audioscrobbler數據集是一個公開發布的數據集,讀者可以在(http://www-etud.iro.umontreal.ca/~bergstj/audioscrobbler_data.html)網站獲取。數據集主要有三部分組成,user_artist_data.txt文件是主要的數據集文件記錄了約2420條用戶id、藝術家id以及用戶收聽藝術家歌曲的次數數據,包含141000個用戶和160萬個藝術家;artist_data.txt文件記錄了藝術家id和對應的名字;artist_alias.txt記錄了藝術家id和對應的別稱id。
推薦算法介紹
由於所選取的數據集只記錄了用戶和歌曲之間的交互情況,除了藝術家名字之外沒有其他信息。因此要找的學習算法不需要用戶和藝術家的屬性信息,這類算法通常被稱為協同過濾。如果根據兩個用戶的年齡相同來判斷他們可能具有相似的偏好,這不叫協同過濾。相反,根據兩個用戶播放過許多相同歌曲來判斷他們可能都喜歡某首歌,這是協調過濾。
本篇所用的算法在數學上稱為叠代最小二乘,把用戶播放數據當成矩陣A,矩陣低i行第j列上的元素的值,代表用戶i播放藝術家j的音樂。矩陣A是稀疏的,絕大多數元素是0,算法將A分解成兩個小矩陣X和Y,既A=XYT,X代表用戶特征矩陣,Y代表特征藝術家矩陣。兩個矩陣的乘積當做用戶-藝術家關系矩陣的估計。可以通過下邊一組圖直觀的反映:
現在假如有5個聽眾,音樂有5首,那麽A是一個5*5的矩陣,假如評分如下:
圖2.1 用戶訂閱矩陣
假如d是三個屬性,那麽X的矩陣如下:
圖2.2 用戶-特征矩陣
Y的矩陣如下:
圖2.3 特征-電影矩陣
實際的求解過程中通常先隨機的固定矩陣Y,則,為提高計算效率,通常采用並行計算X的每一行,既。得到X之後,再反求出Y,不斷的交替叠代,最終使得XYT與A的平方誤差小於指定閾值,停止叠代,得到最終的X(代表用戶特征矩陣)和Y矩陣(代表特征藝術家矩陣)。在根據最終X和Y矩陣結果,向用戶進行推薦。
ALS的Spark實現
Spark MLlib的ALS算法實現有點缺陷,要求用戶和產品的ID必須是數值型,並且是32位非負整數。在計算之前應該首先檢驗一下數據量。
1)數據預處理
過濾無效的用戶藝術家ID和名字行,將格式不正確的數據行剔除掉。
def buildArtistByID(rawArtistData: Dataset[String]): DataFrame = { rawArtistData.flatMap { line => val (id, name) = line.span(_ != ‘\t‘) if (name.isEmpty) { None } else { try { Some((id.toInt, name.trim)) } catch { case _: NumberFormatException => None } } }.toDF("id", "name") }
過濾藝術家id和對應的別名id,將格式拼寫錯誤的行剔除掉。
def buildArtistAlias(rawArtistAlias: Dataset[String]): Map[Int,Int] = { rawArtistAlias.flatMap { line => val Array(artist, alias) = line.split(‘\t‘) if (artist.isEmpty) { None } else { Some((artist.toInt, alias.toInt)) } }.collect().toMap }
將數據轉換成Rating對象,Rating對象是ALS算法對“用戶-產品-值”的抽象。
def buildCounts( rawUserArtistData: Dataset[String], bArtistAlias: Broadcast[Map[Int,Int]]): DataFrame = { rawUserArtistData.map { line => val Array(userID, artistID, count) = line.split(‘ ‘).map(_.toInt) val finalArtistID = bArtistAlias.value.getOrElse(artistID, artistID) (userID, finalArtistID, count) }.toDF("user", "artist", "count") }
2)模型構建
def model( rawUserArtistData: Dataset[String], rawArtistData: Dataset[String], rawArtistAlias: Dataset[String]): Unit = { val bArtistAlias = spark.sparkContext.broadcast(buildArtistAlias(rawArtistAlias)) //藝術家別名數據 val trainData = buildCounts(rawUserArtistData, bArtistAlias).cache() //將數據轉換成需要的格式 val model = new ALS(). setSeed(Random.nextLong()). setImplicitPrefs(true). setRank(10). setRegParam(0.01). setAlpha(1.0). setMaxIter(5). setUserCol("user"). setItemCol("artist"). setRatingCol("count"). setPredictionCol("prediction"). fit(trainData) trainData.unpersist() model.userFactors.select("features").show(truncate = false) val userID = 2093760 val existingArtistIDs = trainData. filter($"user" === userID). select("artist").as[Int].collect() val artistByID = buildArtistByID(rawArtistData) artistByID.filter($"id" isin (existingArtistIDs:_*)).show() val topRecommendations = makeRecommendations(model, userID, 5) topRecommendations.show() val recommendedArtistIDs = topRecommendations.select("artist").as[Int].collect() artistByID.filter($"id" isin (recommendedArtistIDs:_*)).show() model.userFactors.unpersist() model.itemFactors.unpersist() }
本篇文章主要對ALS音樂推薦進行簡單的介紹,下一篇會對模型的參數,以及模型的推薦效果進行評估,並且會對推薦結果進行優化。
多精彩內容,歡迎掃碼關註以下微信公眾號:大數據技術宅。大數據、AI從關註開始
ALS音樂推薦(上)