Spark数据挖掘实例1:基于 Audioscrobbler 数据集音乐推荐
本实例来源于《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 数据集音乐推荐相关推荐
- spark数据挖掘 - 基于 Audioscrobbler 数据集音乐推荐实战
基于 Audioscrobbler 数据集音乐推荐实战 1. 数据集 这个例子将使用 Audioscrobbler 公开的数据集.Audioscrobbler是http://www.last.fm/z ...
- Spotify 每周推荐功能:基于机器学习的音乐推荐
原文地址:Spotify's Discover Weekly: How machine learning finds your new music 原文作者:Sophia Ciocca 译文出自:掘金 ...
- 基于用户的音乐推荐平台
中国石油大学(北京)现代远程教育 毕 业 设 计(论文) 基于用户的音乐推荐平台 姓 名: 学 号: 性 别:
- 《Spark机器学习》笔记——基于MovieLens数据集使用Spark进行电影数据分析
1.数据集下载 https://grouplens.org/datasets/movielens 2.数据集下文件格式 u.user用户属性文件 包含user.id用户ID gender性别 ...
- 数据挖掘实例(基于基站定位数据的商圈分析)
背景: 移动终端的普及,手机用户时间序列的手机定位数据,映射到现实的地理空间位置,即可完整.客观地还原出手机用户的现实活动轨迹,从而挖掘出人口空间分布与活动联系的特征信息. 注:移动通信网络的信号覆盖 ...
- 深度学习 音乐分类_基于神经网络的音乐流派分类
介绍本文讨论神经网络对声音样本的音乐类型进行分类的任务.当我决定在声音处理领域工作时,我认为声音类型分类和图像分类是相同的问题.但是图像非常火热,我并没有找到太多关于深度学习来解决这个问题的相关资料. ...
- 深度学习核心技术精讲100篇(二十九)-基于内容和上下文的音乐推荐
前言 随着在线音乐商城及流媒体音乐服务的出现,数字音乐分发已经使得音乐触手可及.然而,面对突然出现的海量可收听内容,听众很容易面临信息过载的问题.因此,本次分享的主题音乐推荐系统,将为那些面临海量内容 ...
- 协同过滤与隐语义模型推荐系统实例3: 基于矩阵分解(SVD)的推荐
[ 协同过滤与隐语义模型推荐系统实例1: 数据处理 ] [ 协同过滤与隐语义模型推荐系统实例2: 基于相似度的推荐 ] 隐语义模型推荐 基于矩阵分解(SVD)的推荐 # 先计算歌曲被当前用户播放量/用 ...
- Spark大数据分析与实战:基于Spark MLlib 实现音乐推荐
Spark大数据分析与实战:基于Spark MLlib 实现音乐推荐 基于Spark MLlib 实现音乐推荐 一.实验背景: 熟悉 Audioscrobbler 数据集 基于该数据集选择合适的 ML ...
最新文章
- 改变图像,运用match方法判断
- 趁周末,来学点进阶知识:Java 动态编译
- c/c++程序员的技术栈
- ci mysql操作_CI框架数据库各类操作
- 一文搞定Linux shell脚本编程( 史上最全汇总 )
- Java分布式唯一ID生成方案——比UUID效率更高的生成id工具类
- VS code 连接Linux服务器
- 永恒之蓝(MS17010)漏洞复现
- 03. JavaMail 发送HTML邮件
- Qt - 跨平台程序打包发布
- ORACEL R12 总账和子账的关系
- 如何设计出优秀的EDM邮件营销模板
- 01-计算机系统概述
- 海量数据处理-分而治之和hash映射
- Flink 网络流控和反压剖析详解
- 编曲录音宿主软件-Cubase Elements 11 v11.0.30 WiN 元素版
- web 页面接入局域网监控视频
- 股票风险 股票交易上的投机行为往往十分危险。假设某股票行为十分怪异,每天不是涨停(上涨10%)就是跌停(下跌10%)。
- Mysql索引区分度、索引长度
- Android厂商推送冲突了。。(1),2021年Android面试心得