本篇文章的開頭筆者提出一個疑問,何爲數據科學,數據科學是作什麼的?你們帶着這個疑問去讀接下來的這篇音樂推薦的公衆號。html
從經驗上講,推薦引擎屬於大規模機器學習,在平常購物中你們或許深有體會,好比:你在淘寶上瀏覽了一些商品,或者購買了一些商品,那麼淘寶就會根據你的偏好給你推薦一些其餘相似的商品。然而,相比較其餘機器學習算法,推薦引擎的輸出更加的直觀,有時候的推薦效果讓人吃驚。做爲機器學習開篇文章,本篇文章會系統的介紹基於Audioscrobbler數據集的音樂推薦。ios
數據集介紹算法
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。微信
推薦算法介紹dom
因爲所選取的數據集只記錄了用戶和歌曲之間的交互狀況,除了藝術家名字以外沒有其餘信息。所以要找的學習算法不須要用戶和藝術家的屬性信息,這類算法一般被稱爲協同過濾。若是根據兩個用戶的年齡相同來判斷他們可能具備類似的偏好,這不叫協同過濾。相反,根據兩個用戶播放過許多相同歌曲來判斷他們可能都喜歡某首歌,這是協調過濾。機器學習
本篇所用的算法在數學上稱爲迭代最小二乘,把用戶播放數據當成矩陣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音樂推薦進行簡單的介紹,下一篇會對模型的參數,以及模型的推薦效果進行評估,而且會對推薦結果進行優化。
備註:若是文中排版出現錯亂,請點擊https://mp.weixin.qq.com/s/aqF38rDQdT35YrLAyLm-nA
更多精彩內容,歡迎掃碼關注如下微信公衆號:大數據技術宅。大數據、AI從關注開始