【云计算技术】基于 Hadoop 的音乐推荐系统
一、项目背景
随着互联网的普及和音乐流媒体服务的兴起,越来越多的用户可以随时随地通过网络访问各种类型的音乐。然而,音乐市场上的内容和选择变得越来越庞大和复杂,这给用户带来了选择困难和信息过载的问题。为了解决这个问题,音乐推荐系统应运而生。
音乐推荐系统旨在根据用户的兴趣和偏好,提供个性化的音乐推荐,以帮助用户发现新的音乐、艺术家和专辑。通过分析用户的历史行为、喜好和社交网络信息,推荐系统可以推断用户的喜好并向其提供相关的音乐建议。
然而,音乐推荐系统面临着一些挑战。音乐数据的规模庞大,包含了大量的音乐曲目、艺术家信息、用户行为等。处理和分析这些数据需要强大的计算和存储能力。所以,如果我们仅仅采取运行在本地的推荐系统的话,那么实际计算需要耗费的时间可能会非常多。在这样的背景下,基于 Hadoop 的音乐推荐系统应运而生。Hadoop 是一个开源的分布式计算框架,它具有可扩展性、容错性和高性能的特点,非常适合处理大规模的数据集。通过将音乐数据存储在 Hadoop 分布式文件系统(HDFS)中,并利用 Hadoop 的 MapReduce 和Spark 等计算框架,可以有效地处理和分析音乐数据,提取有价值的特征和模式。
综上所述,我们小组在结合实际情况后,对本次项目任务进行了一定的简化和提炼。
首先,获取数据集,并且对数据进行预处理。
其次,根据协同过滤算法对数据进行计算,本地计算需要耗费大量时间。
然后,运用云计算技术,在 Hadoop 上进行运行,减少运行时间。
最后,输出一开始预定需要推荐的音乐编号。
二、实现原理
2.1 基于物品的协同过滤算法(ItemCF)
本次实验采取的 ItemCF 主要分为两个步骤:相似度矩阵的计算、根据相似度矩阵推荐音乐。
2.1.1 计算阶段——相似度矩阵
首先,物品相似度不是音乐本身属性的相似度,而是根据用户对音乐行为的相似度。例如《锦鲤抄》和《牵丝戏》虽然同属“古风”类别,但是可能用户同时对《锦鲤抄》和《小苹果》打分较高,而同时给《锦鲤抄》和《牵丝戏》打分较低,所以最后的相似度矩阵值应该是《锦鲤抄》和《青花瓷》更高一些。为了更加合理的计算相似度矩阵的值,主要考虑用户的两种行为:是否听过、评分高低。
所以根据是否听过,可以获得关于音乐 a,b 的以下两个信息。N(a):听过 a 的用户一共有多少人。共现矩阵 C(a,b):同时听过 a,b 的人一共有多少个。那么这个时候就可以得到一个初步的相似度矩阵的计算公式:
上式中,分母 表示的是喜欢音乐 a 的用户的数量,分子是同时喜欢音乐 a 和 b 的用户数量。
上述公式看起来还是很合理的,但是如果音乐很热门,很多人都喜欢,那么上式中分子与分母就会很接近,此时就会很接近 1。即任何音乐都和热门音乐之间的相似度很高,这会导致 ItemCF 算法会总是推荐热门音乐,这并不是一个好的设计。因此可以采用下面的公式:
那么现在我们就根据提取出来的第一个信息完成了相似度矩阵的初步计算。
2.1.2 推荐阶段:
在推荐阶段,我们如果要给用户 A 推荐音乐,我们就需要根据他听过的音乐的评分去推荐他没听过的音乐。那么我们的推荐值就可以通过下面这个式子来计算:
现在我们把评分考虑进去,rat_dict(A,a)为用户 A 对音乐 a 的评分。那么一个自然而然的想法就是,如果用户给某首音乐的评分很高,那么这首音乐对于其它音乐的相似度贡献应该有所提高,所以我们把推荐值进一步改成了下面这个式子:
这里为了更加直观的理解,我们定义 2.5 分以下的是负面评价,2.5 分以上的是正面评价,分别对应了正系数和负系数。
那么最终,我们就得到了所有没有听过音乐的推荐值 P,将其排序后,推荐出来后即完成任务。
2.2 Hadoop 原理
Hadoop 是一个开源的分布式计算框架,用于处理大规模数据集的存储和分析。它是基于Google 的 MapReduce 和 Google 文件系统(GFS)的论文所描述的原理而开发的。Hadoop的设计目标是能够在廉价的硬件上处理大量数据,并具备高容错性。Hadoop 的核心组件包括以下几个部分:
Hadoop 分布式文件系统(Hadoop Distributed File System,HDFS):HDFS 是 Hadoop的文件系统,它能够将大规模数据集分布式地存储在集群的多个节点上。它的设计目标是在廉价硬件上提供高吞吐量的数据访问,并具备容错性。
MapReduce 编程模型:MapReduce 是 Hadoop 的计算模型,它允许用户编写并行处理大规模数据集的任务。用户只需要实现两个函数:Map 函数将输入数据映射为一系列键值对,Reduce 函数对相同键的值进行聚合。Hadoop 框架会自动处理任务的并行执行、数据的分片和调度等细节。
YARN(Yet Another Resource Negotiator):YARN 是 Hadoop 的资源管理系统,负责集群中各个任务的调度和资源分配。YARN 将计算资源划分为容器,为各个应用程序提供资源,并对资源的使用进行监控和管理。
目前常用的 Hadoop 框架有以下这些:
Hadoop Streaming。提供了使用其他可执行程序来作为 Hadoop 的 mapper 或者 reduce的方式,必须使用规定的语义从标准输入读取数据,然后将结果输出到标准输出。直接使用Streaming 的一个缺点是当 reduce 的输入是按 key 分组的时候,仍然是一行行迭代的,必须由用户来辨识 key 与 key 之间的界限。
mrjob。开源的 Python 框架,封装 Hadoop 的数据流,并积极开发 Yelp 的。由于 Yelp的运作完全在亚马逊网络服务,mrjob 的整合与 EMR 是令人难以置信的光滑和容易(使用boto 包)。
dumbo。同样使用 Hadoop 流包装的框架。dumbo 出现的较早,但由于缺少文档,造成开发困难。这也是不如 mrjob 的一点。dumbo 通过 typedbytes 执行序列化,能允许更简洁的数据传输,也可以更自然的通过指定 JavaInputFormat 读取 SequenceFiles 或者其他格式的文件
hadoopy。是一个兼容 dumbo 的 Streaming 封装,也使用 typedbytes 序列化数据,并直接把 typedbytes 数据写到 HDFS。它有一个很棒的调试机制, 在这种机制下它可以直接把消息写到标准输出而不会干扰 Streaming 过程。它和 dumbo 很相似,但文档要好得多。
pydoop。与其他框架相比,pydoop 封装了 Hadoop 的管道(Pipes),这是 Hadoop的 C++ API。 正因为此,该项目声称他们能够提供更加丰富的 Hadoop 和 HDFS 接口,以及一样好的性能。需要注意 de 所有的输入输出都必须是字符串。
在本次实验我们采用了 mrjob 框架实现推荐系统。mrjob 是一个用于在 Hadoop 集群上运行 MapReduce 作业的 Python 库和框架。它的设计目标是使 Python 开发者能够轻松地编写和运行 MapReduce 作业,而无需深入了解 Hadoop 的底层细节。Mrjob 模型具有的 Python编程模型、跨平台支持、内置的作业生命周期管理、集成的输入输出处理、故障处理和重试机制、集成其他工具和库等特点都可以极大地方面后续编程工作的进行。
下面是使用 mrjob 框架实现上述音乐推荐系统的方法与原理:
- 数据准备与存储:收集音乐相关数据,并进行清洗和预处理,将数据保存在文本文件中,每行表示一个数据记录,格式可以是 CSV 或其他格式。
数据处理与特征提取:
- 使用 mrjob 编写 MapReduce 作业来进行数据处理和特征提取。在 mrjob 中,A 需要定义一个继承自 MRJob 的类,并实现 mapper 和 reducer 方法来定义 Map 和 Reduce 的逻辑。
三、代码分析
3.1 本地运行代码分析:
3.1.1 读入数据:
此处采用了 pandas 库来进行读入,并且使用到了读入文件的三个数据,分别是:用户ID,音乐 ID,评分。
3.1.2 建立相似度矩阵:
按照上一部分的原理,建立相似度矩阵,用以后续的计算。
3.1.3 推荐部分:
在相似度矩阵的基础上,加上评分所带来的权重,最终得到加权后的推荐值 P,将其排序后,完成推荐。
3.2 Hadoop 代码运行分析
我们在华为云平台上购买弹性云服务器(ECS),并选取对象存储服务(OBS)服务,搭建大数据分析基础环境Hadoop分布式集群,并将基于物品的协同过滤推荐算法部署在 Hadoop 中。
代码部分首先需要将先前的python 代码改写为 mapreduce 可适用的 mapper 及 reducer 函数,在这一部分,我们使用mrjob 来编写能在hadoop 运行的程序,也便于在本地调试。
首先,需要导入必要的库,我们使用了 mrjob.job 创建 MapReduce 作业的类与函数,使用 mrjob.step 定义 MapReduce 的作业步骤。
我们首先创建了 MRJob 类 MusicSimilarities(MRJob),继承了 mrjob 库中的 MRJob 类。在这个类中,主要包含了三个步骤的 MapReduce 作业及其他关联函数。
我们将这一实现过程分为 3 步 MapReduce 进行,steps()函数定义了三个步骤的MapReduce 任务,第一步是按照用户创建歌曲及评分键值对,第二步是查找每个用户听过的歌曲,并生成每对音乐的评分对,第三步是计算被多人听过的歌曲的评分之间的相似度。
def steps(self):
return [
MRStep(mapper=self.mapper_parse_input,
reducer=self.reducer_ratings_by_user),
MRStep(mapper=self.mapper_create_item_pairs,
reducer=self.reducer_compute_similarity),
MRStep(mapper=self.mapper_sort_similarities,
reducer=self.reducer_output_similarities)]
接下来是 MapReduce 具体代码实现。
在 Step1 中 , mapper_parse_input() 函 数 将 原 始 数 据 中 的 ( userID, musicID, rating, imestamp)形式转换为(userID,(musicID, rating))的键值对形式。
def mapper_parse_input(self, key, line):
(userID, musicID, rating, timestamp) = line.split(',')
yield userID, (musicID, float(rating))
输入数据如下所示:
输出结果如下所示:
reducer_ratings_by_user()函数对于每个用户,将其评价过的音乐按列表形式聚合,并输出 (userId, [(musicId1, rating1), (musicId2, rating2), …]) 键值对。
def reducer_ratings_by_user(self, user_id, itemRatings):
ratings = []
for musicID, rating in itemRatings:
ratings.append((musicID, rating))
yield user_id, ratings
输入内容为上一阶段的输出值,输出内容如下所示:
在 Step2 中,计算了每对音乐的相似度,先用 mapper_create_item_pairs 函数查找每个用户的听歌记录,生成歌曲对,并将他们的评分对一并作为值输出,其中,由于相似度得分是双向的,因此每一个歌曲对都会输出两个方向的键值对。
def mapper_create_item_pairs(self, user_id, itemRatings):
for itemRating1, itemRating2 in combinations(itemRatings, 2):
musicID1 = itemRating1[0]
rating1 = itemRating1[1]
musicID2 = itemRating2[0]
rating2 = itemRating2[1]
yield (musicID1, musicID2), (rating1, rating2)
yield (musicID2, musicID1), (rating2, rating1)
输入内容为上一阶段的输出值,输出结果如下所示:
然后用 reducer_compute_similarity 函数计算这些音乐对的余弦相似度,产生歌曲对及其相似度得分与评级数,及该评分对出现的次数。
def reducer_compute_similarity(self, musicPair, ratingPairs):
score, numPairs = self.cosine_similarity(ratingPairs)
if (numPairs > 10 and score > 0.95):
yield musicPair, (score, numPairs)
其中调用的 cosine_similarity()函数如下所示:
def cosine_similarity(self, ratingPairs):
numPairs = 0
sum_xx = sum_yy = sum_xy = 0
for ratingX, ratingY in ratingPairs:
sum_xx += ratingX * ratingX
sum_yy += ratingY * ratingY
sum_xy += ratingX * ratingY
numPairs += 1
numerator = sum_xy
denominator = sqrt(sum_xx) * sqrt(sum_yy)
score = 0
if (denominator):
score = (numerator / (float(denominator)))
return (score, numPairs)
输入内容为上一阶段的输出值,输出结果如下所示:
在 Step3 中,输出了每首音乐的相似音乐和相似度,使用 mapper_sort_similarities 函数将键排序,使得相似音乐的相似度成为键。
def mapper_sort_similarities(self, musicPair, scores):
score, n = scores
music1, music2 = musicPair
yield (int(music1), score), (int(music2), n)
再使用 reducer_output_similarities 函数输出相似音乐及其相似度和共同评分数。
def reducer_output_similarities(self, musicScore, similarN):
music1, score = musicScore
for music2, n in similarN:
yield music1, (music2, score, n)
通过以上三个步骤可以得出每首音乐的相似音乐及其对应的相似度。
我们将该程序及数据集上传至云主机,执行以下命令使程序在云主机运行:
1. python MusicSimilarities.py u.data –output-dir /output
执行及输出结果如下所示:
后续,我们将该代码部署到 Hadoop 上执行,执行以下命令:
python MusicSimilarities.py u.data -r hadoop --hadoop-streaming-jar /home/mo
dules/hadoop-2.8.3/share/hadoop/tools/lib/hadoop-streaming-2.8.3.jar --outpu
t-dir /output1
执行及输出结果如下所示:
Step1 输出内容如下所示:
Step2 输出内容如下所示:
- 点赞
- 收藏
- 关注作者
评论(0)