# Spark数据分析之第4课

#音乐推荐和Audioscrobbler数据集
#1. 数据集
http://www-etud.iro.umontreal.ca/~bergstrj/audioscrobbler_data.html

http://www.iro.umontreal.ca/~lisa/datasets/profiledata_06-May-2005.tar.gz

#1.数据
user_artist_data.txt包含141000个用户和160万个艺术家，记录了约2420万条用户播放艺术家歌曲的信息，其中包含播放次数

#2. 交替最小二乘(ALS)推荐算法

#3.准备数据
hdfs dfs -ls /user/ds
-rw-r--r--   1 hdfs supergroup    2932731 2016-05-25 11:49 /user/ds/artist_alias.txt
-rw-r--r--   1 hdfs supergroup   55963575 2016-05-25 11:49 /user/ds/artist_data.txt
-rw-r--r--   1 hdfs supergroup  426761761 2016-05-25 11:50 /user/ds/user_artist_data.txt

Spark MLlib的ALS算法要求用户和产品的ID必须是数值型，并且是32位非负整数。这意味着大于Integer.MAX_VALUE(2147483647)的ID都是非法的。

scala> val rawUserArtistData = sc.textFile("/user/ds/user_artist_data.txt")

val rawUserArtistData = sc.textFile("/user/ds/user_artist_data.txt",8)  #可以设置为集群处理器总核数

#下面计算用户和艺术家的统计信息
scala> rawUserArtistData.map(_.split(' ')(0).toDouble).stats()
res6: org.apache.spark.util.StatCounter = (count: 24296858, mean: 1947573.265353, stdev: 496000.544975, max: 2443548.000000, min: 90.000000)

scala> rawUserArtistData.map(_.split(' ')(1).toDouble).stats()
res7: org.apache.spark.util.StatCounter = (count: 24296858, mean: 1718704.093757, stdev: 2539389.040171, max: 10794401.000000, min: 1.000000)

scala> val rawArtistData = sc.textFile("/user/ds/artist_data.txt")
rawArtistData: org.apache.spark.rdd.RDD[String] = /user/ds/artist_data.txt MapPartitionsRDD[16] at textFile at <console>:28

val artistByID = rawArtistData.map{line =>
val (id,name) = line.span(_ != '\t')
(id.toInt, name.trim)
}
artistByID: org.apache.spark.rdd.RDD[(Int, String)] = MapPartitionsRDD[17] at map at <console>:30

val artistByID = rawArtistData.flatMap{line =>
val (id,name) = line.span(_ != '\t')
if (name.isEmpty) {
None
} else {
try {
Some((id.toInt, name.trim))
} catch {
case e: NumberFormatException => None
}
}
}

artist_alias.txt将拼写错的艺术家ID和非标准的艺术家ID映射为艺术家的正规名字。其中每行有两个ID，用制表符分隔。

val rawArtistAlias = sc.textFile("/user/ds/artist_alias.txt")
val artistAlias = rawArtistAlias.flatMap { line =>
val tokens = line.split("\t")
if (tokens(0).isEmpty) {
None
} else {
Some(tokens(0).toInt, tokens(1).toInt)
}
}.collectAsMap() #collectAsMap 对(K,V)型的RDD数据返回一个单机HashMap。对于重复K的RDD元素，后面的元素覆盖前面的元素。
artistAlias: scala.collection.Map[Int,Int] = Map(6803336 -> 1000010, 6663187 -> 1992, 2124273 -> 2814, 10412283 -> 1...

res15: String = Aerosmith (unplugged)

res16: String = Aerosmith

#4 构建第一个模型

import org.apache.spark.mllib.recommendation._
val  trainData = rawUserArtistData.map { line =>
val Array(userID,artistID,count) = line.split(' ').map(_.toInt)
val finalArtistID = bArtistAlias.value.getOrElse(artistID,artistID)
Rating(userID,finalArtistID,count)
}.cache

#如果艺术家存在别名，取得艺术家的别名，否则取得原始名字

#广播变量
Spark执行一个阶段时(Stage)，会为待执行函数建立闭包，也就是该阶段所有任务所需信息的二进制形式。这个闭包包括驱动程序里函数引用的所有数据结构。
Spark把这个闭包发送到集群的每个executor上。

#最后，我们构建模型
scala> val model = ALS.trainImplicit(trainData,10,5,0.01,1.0)

scala> model.userFeatures.mapValues(_.mkString(", ")).first()
res11: (Int, String) = (90,-0.07274463027715683, 0.46505239605903625, 0.4246442914009094, 0.017407121136784554, -0.26859962940216064, -1.047317624092102, -0.656421959400177, -0.059907883405685425, -0.046106256544589996, -0.04561980441212654)

trainImplicit()中包含的其他参数都是超参数，他们的值将影响模型的推荐质量。

#逐个检查推荐结果

scala> val rawArtistsForUser = rawUserArtistData.map(_.split(' ')).filter {case Array(user,_,_) => user.toInt == 2093760}
rawArtistsForUser: org.apache.spark.rdd.RDD[Array[String]] = MapPartitionsRDD[146] at filter at <console>:26

scala> val existingProducts = rawArtistsForUser.map{ case Array(_,artist,_) => artist.toInt }.collect().toSet
existingProducts: scala.collection.immutable.Set[Int] = Set(1255340, 942, 1180, 813, 378)

scala> rawArtistsForUser.toDebugString
res19: String =
(4) MapPartitionsRDD[143] at filter at <console>:26 []
|  MapPartitionsRDD[142] at map at <console>:26 []
|  /user/ds/user_artist_data.txt MapPartitionsRDD[1] at textFile at <console>:21 []
|  /user/ds/user_artist_data.txt HadoopRDD[0] at textFile at <console>:21 []

scala> artistByID.filter { case (id,name) => existingProducts.contains(id)}.values.collect().foreach(println)
David Gray
Blackalicious
Jurassic 5
The Saw Doctors
Xzibit

#下面我们对5个用户做出推荐:
scala> val recommendations = model.recommendProducts(2093760,5)
recommendations: Array[org.apache.spark.mllib.recommendation.Rating] = Array(Rating(2093760,2814,0.0324184709372749), Rating(2093760,1001819,0.03097342837980681), Rating(2093760,1300642,0.030952322282742546), Rating(2093760,1007614,0.03035531332876626), Rating(2093760,4605,0.030150220216114847))

scala> recommendations.foreach(println)
Rating(2093760,2814,0.0324184709372749)
Rating(2093760,1001819,0.03097342837980681)
Rating(2093760,1300642,0.030952322282742546)
Rating(2093760,1007614,0.03035531332876626)
Rating(2093760,4605,0.030150220216114847

scala> val recommendedProductIDs = recommendations.map(_.product).toSet
recommendedProductIDs: scala.collection.immutable.Set[Int] = Set(2814, 1001819, 1300642, 4605, 1007614)

scala> artistByID.filter{ case(id,name) => recommendedProductIDs.contains(id) }.values.collect().foreach(println)
50 Cent
Snoop Dogg
Jay-Z
2Pac
The Game

• 本文已收录于以下专栏：

## spark机器学习点击量推荐

• wuguobanga
• 2016年03月01日 16:59
• 945

## Spark高级数据分析

• MrGeroge
• 2017年12月07日 10:01
• 248

## spark高级数据分析系列之第三章音乐推荐和 Audioscrobbler 数据集

3.1数据集和整体思路 数据集 本章实现的是歌曲推荐，使用的是ALS算法，ALS是spark.mllib中唯一的推荐算法，因为只有ALS算法可以进行并行运算。 使用数据集在这里，里面包含该三个文件...
• u014779006
• 2017年07月14日 08:47
• 470

## 【spark】sprak-scala推荐算法实现

package example /** * Created by zhangyaran on 2017/10/30. */ import scala.collection.Map impo...
• ranran0224
• 2017年11月22日 11:44
• 146

## Spark数据分析之第5课

• jiangshouzhuang
• 2016年06月02日 23:49
• 1189

## RDD原文翻译

• weiying7
• 2016年04月26日 09:51
• 771

## 3-Spark高级数据分析-第三章 音乐推荐和Audioscrobbler数据集

• Next__One
• 2017年10月24日 16:48
• 253

## 音乐推荐&Audioscrobbler数据集

• haohaixingyun
• 2016年09月26日 21:04
• 1387

## 音乐推荐数据集Million Song Dataset

• 2017年01月05日 16:30
• 1847

## 基于近邻用户协同过滤算法的音乐推荐系统

0. 摘 要基于近邻用户的协同过滤音乐推荐系统，主要是将与目标用户有相同行为和兴趣爱好的用户，形成一个最近邻的推荐群组，从最近邻推荐群组中产生最终的目标用户推荐列表。该推荐系统通过网络爬虫获取虾米音乐...
• ZCF1002797280
• 2016年03月03日 11:04
• 4425

举报原因： 您举报文章：Spark数据分析之第4课 色情 政治 抄袭 广告 招聘 骂人 其他 (最多只允许输入30个字)