本实例来源于《Spark高级数据分析》,这是一个很好的spark数据挖掘的实例。从经验上讲,推荐引擎属于大规模机器学习,在日常购物中大家或许深有体会,比如:你在淘宝上浏览了一些商品,或者购买了一些商品,那么淘宝就会根据你的偏好给你推荐一些其他类似的商品。然而,相比较其他机器学习算法,推荐引擎的输出更加的直观,有时候的推荐效果让人吃惊。作为机器学习开篇文章,本篇文章会系统的介绍基于Audioscrobbler数据集的音乐推荐。

数据集介绍

Audioscrobbler数据集是一个公开发布的数据集,读者可以在(https://github.com/libaoquan95/aasPractice/tree/master/c3/profiledata_06-May-2005)网站获取。数据集主要有三部分组成,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矩阵结果,向用户进行推荐。

数据准备

首先将样例数据上传到HDFS,如果想要在本地测试这些功能的话,需要内存数量至少 6g, 当然可以通过减少数据量来达到通用的测试。

object RunRecommender {def main(args: Array[String]): Unit = {val conf = new SparkConf();conf.setMaster("local[*]")val spark = SparkSession.builder().config(conf).getOrCreate()// Optional, but may help avoid errors due to long lineage// spark.sparkContext.setCheckpointDir("hdfs:///tmp/")spark.sparkContext.setCheckpointDir("d:///tmp/")//val base = "hdfs:///user/ds/"val base =  "E:/newcode/spark/aas/data/";val rawUserArtistData = spark.read.textFile(base + "user_artist_data.txt")val rawArtistData = spark.read.textFile(base + "artist_data.txt")val rawArtistAlias = spark.read.textFile(base + "artist_alias.txt")val runRecommender = new RunRecommender(spark)runRecommender.preparation(rawUserArtistData, rawArtistData, rawArtistAlias)runRecommender.model(rawUserArtistData, rawArtistData, rawArtistAlias)runRecommender.evaluate(rawUserArtistData, rawArtistAlias)runRecommender.recommend(rawUserArtistData, rawArtistData, rawArtistAlias)}}
def preparation(rawUserArtistData: Dataset[String],rawArtistData: Dataset[String],rawArtistAlias: Dataset[String]): Unit = {rawUserArtistData.take(5).foreach(println)val userArtistDF = rawUserArtistData.map { line =>val Array(user, artist, _*) = line.split(' ')(user.toInt, artist.toInt)}.toDF("user", "artist")userArtistDF.agg(min("user"), max("user"), min("artist"), max("artist")).show()val artistByID = buildArtistByID(rawArtistData)val artistAlias = buildArtistAlias(rawArtistAlias)val (badID, goodID) = artistAlias.headartistByID.filter($"id" isin (badID, goodID)).show()
}
/*** 过滤无效的用户艺术家ID和名字行,将格式不正确的数据行剔除掉。* @param rawArtistData* @return*/
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,将格式拼写错误的行剔除掉。* @param rawArtistAlias* @return*/
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
}

代码中模型训练好之后,预测了用户 2093760 的推荐结果,我测试结果如下,由于里面代码使用了随机生成初始矩阵,每个人的结果都有可能不一样。

Some((2814,50 Cent))
Some((829,Nas))
Some((1003249,Ludacris))
Some((1001819,2Pac))
Some((1300642,The Game))

代码中也给出了该用户以前听过的艺术家的名字如下:

Some((1180,David Gray))
Some((378,Blackalicious))
Some((813,Jurassic 5))
Some((1255340,The Saw Doctors))
Some((942,Xzibit))

模型评价

auc评价方法

def areaUnderCurve(positiveData: DataFrame,bAllArtistIDs: Broadcast[Array[Int]],predictFunction: (DataFrame => DataFrame)): Double = {// What this actually computes is AUC, per user. The result is actually something// that might be called "mean AUC".// Take held-out data as the "positive".// Make predictions for each of them, including a numeric scoreval positivePredictions = predictFunction(positiveData.select("user", "artist")).withColumnRenamed("prediction", "positivePrediction")// BinaryClassificationMetrics.areaUnderROC is not used here since there are really lots of// small AUC problems, and it would be inefficient, when a direct computation is available.// Create a set of "negative" products for each user. These are randomly chosen// from among all of the other artists, excluding those that are "positive" for the user.val negativeData = positiveData.select("user", "artist").as[(Int,Int)].groupByKey { case (user, _) => user }.flatMapGroups { case (userID, userIDAndPosArtistIDs) =>val random = new Random()val posItemIDSet = userIDAndPosArtistIDs.map { case (_, artist) => artist }.toSetval negative = new ArrayBuffer[Int]()val allArtistIDs = bAllArtistIDs.valuevar i = 0// Make at most one pass over all artists to avoid an infinite loop.// Also stop when number of negative equals positive set sizewhile (i < allArtistIDs.length && negative.size < posItemIDSet.size) {val artistID = allArtistIDs(random.nextInt(allArtistIDs.length))// Only add new distinct IDsif (!posItemIDSet.contains(artistID)) {negative += artistID}i += 1}// Return the set with user ID added backnegative.map(artistID => (userID, artistID))}.toDF("user", "artist")// Make predictions on the rest:val negativePredictions = predictFunction(negativeData).withColumnRenamed("prediction", "negativePrediction")// Join positive predictions to negative predictions by user, only.// This will result in a row for every possible pairing of positive and negative// predictions within each user.val joinedPredictions = positivePredictions.join(negativePredictions, "user").select("user", "positivePrediction", "negativePrediction").cache()// Count the number of pairs per userval allCounts = joinedPredictions.groupBy("user").agg(count(lit("1")).as("total")).select("user", "total")// Count the number of correctly ordered pairs per userval correctCounts = joinedPredictions.filter($"positivePrediction" > $"negativePrediction").groupBy("user").agg(count("user").as("correct")).select("user", "correct")// Combine these, compute their ratio, and average over all usersval meanAUC = allCounts.join(correctCounts, Seq("user"), "left_outer").select($"user", (coalesce($"correct", lit(0)) / $"total").as("auc")).agg(mean("auc")).as[Double].first()joinedPredictions.unpersist()meanAUC
}完整代码下载:RunRecommender.scala

Spark数据挖掘实例1:基于 Audioscrobbler 数据集音乐推荐相关推荐

  1. spark数据挖掘 - 基于 Audioscrobbler 数据集音乐推荐实战

    基于 Audioscrobbler 数据集音乐推荐实战 1. 数据集 这个例子将使用 Audioscrobbler 公开的数据集.Audioscrobbler是http://www.last.fm/z ...

  2. Spotify 每周推荐功能:基于机器学习的音乐推荐

    原文地址:Spotify's Discover Weekly: How machine learning finds your new music 原文作者:Sophia Ciocca 译文出自:掘金 ...

  3. 基于用户的音乐推荐平台

    中国石油大学(北京)现代远程教育 毕 业 设 计(论文) 基于用户的音乐推荐平台 姓    名: 学    号: 性    别:

  4. 《Spark机器学习》笔记——基于MovieLens数据集使用Spark进行电影数据分析

    1.数据集下载 https://grouplens.org/datasets/movielens 2.数据集下文件格式 u.user用户属性文件 包含user.id用户ID    gender性别   ...

  5. 数据挖掘实例(基于基站定位数据的商圈分析)

    背景: 移动终端的普及,手机用户时间序列的手机定位数据,映射到现实的地理空间位置,即可完整.客观地还原出手机用户的现实活动轨迹,从而挖掘出人口空间分布与活动联系的特征信息. 注:移动通信网络的信号覆盖 ...

  6. 深度学习 音乐分类_基于神经网络的音乐流派分类

    介绍本文讨论神经网络对声音样本的音乐类型进行分类的任务.当我决定在声音处理领域工作时,我认为声音类型分类和图像分类是相同的问题.但是图像非常火热,我并没有找到太多关于深度学习来解决这个问题的相关资料. ...

  7. 深度学习核心技术精讲100篇(二十九)-基于内容和上下文的音乐推荐

    前言 随着在线音乐商城及流媒体音乐服务的出现,数字音乐分发已经使得音乐触手可及.然而,面对突然出现的海量可收听内容,听众很容易面临信息过载的问题.因此,本次分享的主题音乐推荐系统,将为那些面临海量内容 ...

  8. 协同过滤与隐语义模型推荐系统实例3: 基于矩阵分解(SVD)的推荐

    [ 协同过滤与隐语义模型推荐系统实例1: 数据处理 ] [ 协同过滤与隐语义模型推荐系统实例2: 基于相似度的推荐 ] 隐语义模型推荐 基于矩阵分解(SVD)的推荐 # 先计算歌曲被当前用户播放量/用 ...

  9. Spark大数据分析与实战:基于Spark MLlib 实现音乐推荐

    Spark大数据分析与实战:基于Spark MLlib 实现音乐推荐 基于Spark MLlib 实现音乐推荐 一.实验背景: 熟悉 Audioscrobbler 数据集 基于该数据集选择合适的 ML ...

最新文章

  1. 改变图像,运用match方法判断
  2. 趁周末,来学点进阶知识:Java 动态编译
  3. c/c++程序员的技术栈
  4. ci mysql操作_CI框架数据库各类操作
  5. 一文搞定Linux shell脚本编程( 史上最全汇总 )
  6. Java分布式唯一ID生成方案——比UUID效率更高的生成id工具类
  7. VS code 连接Linux服务器
  8. 永恒之蓝(MS17010)漏洞复现
  9. 03. JavaMail 发送HTML邮件
  10. Qt - 跨平台程序打包发布
  11. ORACEL R12 总账和子账的关系
  12. 如何设计出优秀的EDM邮件营销模板
  13. 01-计算机系统概述
  14. 海量数据处理-分而治之和hash映射
  15. Flink 网络流控和反压剖析详解
  16. 编曲录音宿主软件-Cubase Elements 11 v11.0.30 WiN 元素版
  17. web 页面接入局域网监控视频
  18. 股票风险 股票交易上的投机行为往往十分危险。假设某股票行为十分怪异,每天不是涨停(上涨10%)就是跌停(下跌10%)。
  19. Mysql索引区分度、索引长度
  20. Android厂商推送冲突了。。(1),2021年Android面试心得

热门文章

  1. ai 计算机视觉_人工智能中的计算机视觉
  2. 最短路小结(三种算法+各种常见变种)
  3. Android Apk 签名方案
  4. spin_lock浅析
  5. 中国数字银行春季论坛热议高质量发展 金融科技破解资产负债管理难题
  6. KOL营销之痛点难点
  7. MATLAB(九)数值微积分
  8. 【PyTorch】torch.nn.Transformer解读与应用
  9. web前端工程师都做什么工作
  10. 2022济南大学acm新生赛题解