1. 程式人生 > >ALS音樂推薦(上)

ALS音樂推薦(上)

優化 播放 lazy 淘寶 empty tex max taf 分享圖片

  本篇文章的開頭筆者提出一個疑問,何為數據科學,數據科學是做什麽的?大家帶著這個疑問去讀接下來的這篇音樂推薦的公眾號。

  從經驗上講,推薦引擎屬於大規模機器學習,在日常購物中大家或許深有體會,比如:你在淘寶上瀏覽了一些商品,或者購買了一些商品,那麽淘寶就會根據你的偏好給你推薦一些其他類似的商品。然而,相比較其他機器學習算法,推薦引擎的輸出更加的直觀,有時候的推薦效果讓人吃驚。作為機器學習開篇文章,本篇文章會系統的介紹基於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音樂推薦(上)