admin管理员组

文章数量:1663061

物以类聚:Kmean 聚类算法

在开始之前,先来看看上个课时的思考题。在配置分类器时,我们需要设置的参数主要有:

  • 树的个数;

  • 树的最大深度;

  • 特征子集选取策略;

  • 纯度。

在特征子集的选取策略中可以配置信息增益、信息增益率以及基尼系数。

然后我们开始今天的课程。上一课时中介绍了随机森林和决策树分类器,它们都属于监督学习,本节将介绍聚类算法,它属于机器学习的另一种应用——无监督学习

物以类聚

通俗地说,人们习惯将相似的东西归为一类,这就是“物以类聚”。先来看几个例子,用苹果举例,如下图所示。

一共有 6 个苹果,如果将这些苹果分类,我们可以很自然地将其分为两类,因为前 4 个的大小明显和后面不同。再来看看另外 6 个苹果,如下图所示。

对这 6 个苹果进行分类,同样很容易分为两类,前 4 个苹果的颜色和后面的明显不同。在上面的两次分类过程中,已经完成了两次聚类,聚类的依据第一次是大小,第二次是颜色。接下来看看最后 6 个苹果,如下图所示。

现在还能一口气将这 6 个苹果进行分类吗?这 6 个苹果有大有小,有红有绿,确实令人困惑,如果再加上第 2 个和第 5 个苹果很甜、其余苹果很酸等特征,问题就更棘手了。人对于单一维度的数据很敏感,维度一多就有些力不从心,而计算机恰恰擅长处理这些数据,聚类算法就是“物以类聚”这一朴素思想的数学体现。

聚类是把相似的对象通过静态分类的方法分成不同的组别或者更多的子集,使得同一个子集中的成员对象都有相似的一些属性,常见的包括在坐标系中更加短的空间距离。一般把聚类归纳为一种非监督式学习,它与分类算法最大的不同在于训练集没有标签。

K 均值聚类算法

到目前为止,我们对聚类的理解可以用下图来表示。聚类的目标就是将相似的物体进行分组,并将其标注,图中的相似性体现在点与点之间的距离。

K 均值算法是一种被广泛使用的直接聚类算法,位列数据挖掘十大算法之二,可见其影响力。 K 均值算法是一种迭代型聚类算法,它将一个给定的数据集分为用户指定的 K 个聚簇,速度较快,易于修改。从上面这句话可以得出 K 均值算法的输入对象为数据集 D 和一个非常关键的 K 值, K 值也就是用户认为数据集 D 应该被分为几类,数据集 D 可以被认为是 d 维向量空间中的一些点( D ={ xi | i = 1,…, N },其中 xi∈Rd 表示数据集 D 中第 i 个对象)。

K 均值算法中,每个聚簇都用一个点来代表,这些聚簇用集合 C ={ cj | j = 1,…, k } 来表示,这 K 个代表聚簇有时也被称为聚簇均值或聚簇中心。聚类算法通常用相似度的概念对点集进行分组,具体到 K 均值算法,默认的相似度标准为欧氏距离。 K 均值的实质是要最小化一个如下的非负代价函数:

换言之, K 均值的最小化目标是每个点 xi 和离它最近的聚簇中心 cj 之间的欧氏距离的平方和,这也是 K 均值的目标函数。

下面是 K 均值算法的伪代码

输入:数据集 D,聚簇数 k

输出:聚簇代表集合 C,聚簇成员向量 m

/初始化聚簇代表 C/

从数据集 D 中随机挑选 k 个数据点3

使用这 k 个数据点构成初始聚簇代表集合 C

repeat

/再分数据/

将 D 中的每个数据点重新分配至与之最近的聚簇均值

更新 m(mi 表示D中第 i 个点的聚簇标识)

/重定均值/

更新 C(cj 表示第 j 个聚簇均值)

until 目标函数

收敛

从上面的伪代码可以看出,算法主要包括两个交替执行的步骤,即再分数据和重定均值,并且是通过随机选取 K 个点来启动算法。

再分数据指的是:将每个数据点分配到当前与之最近的那个聚簇中心,同时打破了上次迭代确定的归属关系。这一步会对全部数据进行一个新的划分。

重定均值指的是:重新确定每一个聚簇中心,即计算所有分配给该聚簇的数据点的中心(如算术平均值),这也是 K 均值的名称由来。

当满足收敛条件时,算法停止。

鸢尾花数据集进行聚类

本节将介绍用 K 均值算法对鸢尾花数据集(也就是预处理课时中用到的 iris 数据集)进行聚类的过程,在聚类之前,需要先对特征用 PCA 进行降维,用 Z 分数进行归一化。由于鸢尾花数据本身带有标签列(Species,一共有 3 种类型,即 setosa 、 versicolor 和 virginica ),因此最后可以用该列来评估聚类效果,代码如下:

import org.apache.spark.ml.feature.{PCAVectorAssembler} 
import org.apache.spark.sql.{DatasetRowSparkSession} 
import org.apache.spark.ml.feature.StandardScaler 
import org.apache.spark.sql.types.{DoubleTypeStringTypeStructFieldStructType} 
import org.apache.spark.ml.Pipeline 
import org.apache.spark.ml.feature.MinMaxScaler 
import org.apache.spark.ml.clustering.KMeans 
import org.apache.spark.ml.evaluation.ClusteringEvaluator 

object IRISKmeans {

  def main(args: Array[String]): Unit = {

    val spark = SparkSession
    .builder()
    .master(“local[2]”)
    .appName(“IRISKmeans”)
    .getOrCreate()

    // iris数据集数据结构
    val fields = Array(“id”,“SepalLength”,“SepalWidth”,“PetalLength”,“PetalWidth”,“Species”)

    val fieldsType = fields.map(
      r => if (r  “id”||r  “Species”)
             {StructField(r, StringType)}
           else
             {StructField(r, DoubleType)}
    )

    val schema = StructType(fieldsType)

    val featureCols = Array(“SepalLength”,“SepalWidth”,“PetalLength”,“PetalWidth”)

    val data = spark.read.schema(schema)
    .option(“header”false)
    .csv(“data/iris/”)

    val vectorAssembler = new VectorAssembler()
    .setInputCols(featureCols)
    .setOutputCol(“features”)

    val pca = new PCA()
    .setInputCol(“features”)
    .setOutputCol(“pcaFeatures”)
    // 主成分个数,也就是降维后的维数
    .setK(2)

    val scaler = new StandardScaler()
    .setInputCol(“features”)
    .setOutputCol(“scaledFeatures”)
    .setWithStd(true)
    .setWithMean(false)

    // 设置Kmeans参数
    val kmeans = new KMeans()
    .setK(3)
    .setSeed(System.currentTimeMillis())
    .setMaxIter(10)

    val pipeline = new Pipeline()
    .setStages(Array(vectorAssembler,pca,scaler))

    val model = pipeline.fit(data)

    val predictions = model.transform(data)

    predictions.show()

    val evaluator = new ClusteringEvaluator()

    val silhouette = evaluator.evaluate(predictions)

    println(s"Silhouette with squared euclidean distance = $silhouette")

  }

}

下面是部分的聚类结果:


……


……

可以看到,聚类结果为 setosa 类型的样本全部被归到类 1,无一例外;versicolor 类型的样本大部分被归到类 2,极少部分被归到类 0;virginica 类型的样本大部分被归到类 0,少部分被归到类 2,结果基本正确。

评估聚类结果

聚类是一种典型的无监督学习,与监督学习很容易评估模型性能不同,无监督学习通常没有一个标准。那么如何才能判断聚类的结果是优质的,这同样是一个值得讨论的问题。我们先来看一份数据,假设一份数据在数据空间中均匀分布,如下图所示:

那么可以想象这份数据的聚类结果质量一定不高,数据的均匀分布导致这些聚簇没有任何实际意义。我们还可以通过霍普金斯统计量来检测变量的空间随机性,这里就不展开讲解了。

一般来说,我们可以通过簇间距离和簇内距离来评估聚类质量,簇间距离指的是两个簇中心之间的距离,而簇内距离是指簇内部成员间的距离。 我们可以这样判断一次优质的聚类结果,如下图所示,有较大的簇间距离和较小的簇内距离,也就是说,优质的聚类是簇中的点尽量紧密,而簇和簇之间尽量远离。

小结

本课时学习了另一类有代表性的无监督学习算法:聚类算法。在编写 Pipeline 时,我们也用到了前面学习的 Transformer 来进行降维和归一化处理,有了前面的基础,相信同学们对预处理会有更深的理解。

最后给大家留个思考题:如果不进行归一化处理,那么会对聚类结果有什么影响呢?


推荐引擎:协同过滤

在开始今天的课程前,我们先来看看上节课的习题:如果不进行归一化处理,那么会对聚类结果有什么影响呢? 答案是聚类结果会被数值最大的那个维度所左右。

然后我们进行今天的课程学习。推荐系统是计算广告中的重要组成部分,也是典型的大数据应用。在大数据时代,我们真正需要或者感兴趣的物品和信息淹没在了大量的数据中,使得很难发现它们。为了解决信息检索的问题,在门户网站的早期,主要使用分类目录让用户快速找到所需要的信息,接着门户网站就演变出了搜索引擎。和单纯使用分类目录相比,搜索引擎无疑是更加聪明的,它帮助用户查询给定的信息,并为用户返回结果。分类目录是静态的、覆盖率低的索引,搜索引擎则采取主动并且针对全网的索引策略,直到现在,搜索引擎也是信息检索最主流的方案。在累积了大量的用户行为数据后,推荐系统诞生了,它不需要用户主动搜索,系统会将它认为用户感兴趣的信息主动推送给用户,因此推荐系统的核心在于从海量数据中将每个用户感兴趣的信息筛选出来。

协同过滤算法是诞生最早并且非常著名的推荐算法,是很多电商平台(如亚马逊、京东等)推荐系统的核心。算法通过对用户的历史行为数据进行分析来发现用户的偏好,然后基于不同的偏好对用户进行分组,并推荐相似的商品,因此协同指的是借鉴与你相似的人的观点进行推荐。协同过滤主要分为 3 类:基于用户的协同过滤、基于商品的协同过滤和基于模型的协同过滤。这也是本课时的主要内容。

基于用户的协同过滤

基于用户的协同过滤的核心是根据用户对商品的偏好,来计算不同用户之间的相似性,并在相似的用户之间进行推荐,如下图所示。

在上图中,用户 1 买了商品 1、2、3、4,而用户 3 的历史数据表明他买了商品 2、3,这样我们就认为用户 1、3 是一类用户。于是,就可以将用户 1 买过而用户 3 没有买过的商品 1、4 推荐给用户 3。

完成这个工作的第一步是根据用户行为的历史数据找到用户偏好的数据。表明用户偏好的方式可以有很多种,如转发、点击和购买等。我们用以下表格整理了判断用户偏好的主要方式及其类型和特征,比如对物品的满意程度评分:1~5 分,满分是 5 分。

接着需要将这些行为统统量化为一个值,例如加权平均等,这样,我们会得到一个矩阵,如下表所示。

用户的数量决定了矩阵的行数,商品的数量决定了矩阵的列数。可以想到,在类似于淘宝、京东这样的平台,这个矩阵是非常巨大并且稀疏的。构建好这样的矩阵后,我们就可以利用数学的方法来比较相似性,常见的相似性度量有以下几种方法。

  • 欧氏距离:欧氏距离是所有距离测度中最简单的,也是最基本的。数学上,两个 n 维向量 (a1,a2,…,an)和(b1,b2,…,bn) 之间的欧氏距离公式为:

  • 平方欧氏距离:如名称所示,平方欧氏距离的值为欧氏距离的平方,两个 n 维向量 (a1,a2,…,an)和(b1,b2,…,bn) 之间的平方欧氏距离公式为:

  • 曼哈顿距离:两个点之间的曼哈顿距离是它们坐标差的绝对值之和,如下图所示。

点(2,2)与点(6,6)之间的欧氏距离为 5.65(虚线所示),曼哈顿距离为 8(实线所示)。两个 n 维向量 (a1,a2,…,an)和(b1,b2,…,bn) 之间的曼哈顿距离公式为:

  • 余弦距离:余弦距离是用向量空间中两个向量夹角的余弦值,作为两个个体间差异大小的度量。余弦距离需要将待比较的两个点视为从原点指向它们的向量,向量的夹角为 θ,如下图所示。

两个 n 维向量 (a1,a2,…,an)和(b1,b2,…,bn) 之间的余弦距离公式为:

d = 1−cos θ

其中

余弦距离一般用在文本聚类中。

如果我们选择欧氏距离作为相似性度量,用户两两之间的距离和距离倒数就可以填入下表中,我制作了一个空表格来举例。

两个用户越不相似,则他们之间的距离越远、距离值越大,这对于相似性比较不够直观,因此我们通常采用距离倒数来表现相似性,也就是说越相似,其值越小。

假设与用户 3 最相似的用户是用户 1,这样我们就可以根据用户 1 的行为来对用户 3 进行推荐,但要注意的是,不要推荐用户 3 购买过的商品。从上面这个过程可以看到,协同过滤的大致步骤是首先进行收集数据,其次计算相似度,最后进行推荐

基于用户的协同过滤需要累积一定的用户行为数据,因此对新网站不是特别友好。另外,通常来说一个大型的电子商务网站,每个用户的商品占比是极少的,这导致了不同用户之间的购买商品重叠较少,算法可能无法找到与某个用户相似的邻居。这种协同过滤需要耗费大量的计算资源,最坏的情况下,协同过滤的复杂度是 O(m × n),其中 m 是用户数量,n 是商品数量,但由于在实际情况中,大量用户只购买过很少一部分商品,因此实际的计算复杂度趋近于 O(m + n)。

基于商品的协同过滤

基于商品的协同过滤与基于用户的协同过滤很相似,核心是基于相似商品对用户进行推荐。如下图所示,如果用户 1 同时购买了商品 1 和 3,那么认为商品 1 和 3 有很强的相关性,那么在用户 3 购买商品 3 时,会给他推荐商品 1。

基于商品的协同过滤的基本步骤依然是:收集数据,计算相似度,进行推荐。在收集数据时,表现形式与表 2 类似,只需将矩阵行列进行转置,如下表所示。

同理,基于这个矩阵,可以得到商品与商品之间的关联性(欧氏距离),如下表所示。

当用户购买了某个商品后,检索上表的结果,将把和该商品关联性最大的商品推荐给用户。

基于商品的协同过滤相比于基于用户的协同过滤来说,更加稳定,初期可以凭经验预设一小部分商品之间的相关性,也能得到不错的效果。

两种协同过滤的对比

基于用户的协同过滤很早就提出了,而基于商品的协同过滤是由亚马逊在 2001 年左右发表的论文中提出并开始流行。那具体到不同的场景中,我们究竟该如何选择过滤方式呢?下面分别从计算复杂度、适用场景、推荐多样性、算法适应度来进行对比。

计算复杂度

一般来说,大型电子商务平台的用户数量往往大大超过商品的数量,商品数量也会相对稳定,因此计算商品之间的相似度不但计算量较小,同时也不必频繁更新。但这只是针对购物平台来说,对于新闻、博客、微博等内容平台来说恰恰相反,用户的数量是一定的,而内容则是海量的,也是频繁更新的,因此从计算复杂度来说,需要根据具体场景具体分析

适用场景

在类似于京东这样的购物网站,如果采取基于用户的协同过滤或许有些莫名其妙, 比如向某位用户推荐了一本书,给出的解释是和你相似的某位用户也在看这本书,但这两位用户很可能不认识,但如果根据用户的浏览记录推荐便令人信服。因此在非社交类的平台上,基于商品的协同过滤通常效果更好,而在社交类平台选择基于用户的协同过滤似乎更合理。

性能

推荐引擎的性能主要通过精度多样性两个指标评估。研究发现在相同的数据集上同时使用基于用户和基于商品的协同过滤算法,推荐列表中只有 50% 是一样的,其余的则完全不同,但是这两个算法都有相似的精度,因此这两种协同过滤很适合互为补充。

单从一个用户的角度来讲,推荐系统的多样性指的是给定一个用户,看推荐列表中的商品是否多样化,也就是要比较推荐物品之间的两两相似度。这样说来,基于商品的协同过滤显然不如基于用户的协同过滤好,因为基于商品的协同过滤就是因为相似才推荐的。

而从系统的角度来说,推荐系统的多样性指的是能够给所有用户提供丰富的选择。在这种情况下,基于商品的协同过滤要远远好于基于用户的协同过滤,因为基于用户的协同过滤推荐的总是最热门的。

从上面的分析可以知道,这两种推荐方法都有其合理性,但都不是最完美的。最好的选择是将它们结合使用,当采用基于商品的协同过滤导致了系统对个人推荐的多样性不足时,可以通过加入基于用户的协同过滤增加个人推荐的多样性,从而提高精度。而当因为采用基于用户的协同过滤而使系统的整体多样性不足时,可以通过加入基于商品的协同过滤增加整体的多样性,也可以提高推荐的精度。

算法适应度

前面都是从推荐引擎本身来进行考虑,而算法适应度则是站在用户的角度来比较这两种不同推荐的效果。作为一个用户,如果采用基于用户的协同过滤算法进行推荐,而与这位用户有着相同喜好的用户的数量又很少,可想而知这样的效果是很差的,因为基于用户的协同过滤中有个很重要的假设就是该用户的爱好和与他相似的用户的爱好相似,所以基于用户的协同过滤的算法适应度是和与这位用户有共同爱好的用户数量成正比的。

而基于商品的协同过滤也有一个假设:用户会喜欢与他以前喜欢的东西相似的东西,那么我们可以计算一个用户喜欢的物品的自相似度。一个用户喜欢的商品的自相似度大,就说明他喜欢的东西都是比较相似的,也就是说他比较满足这个基本假设,那么他对基于商品的协同过滤的适应度自然比较好;反之,如果喜欢的商品自相似度小,就说明这个用户的喜好习惯并不满足这个基本假设,那么对于这种用户,用基于商品的协同过滤做出好的推荐的可能性非常低。

基于模型的协同过滤

Netflix 公司曾经举办过一次竞赛,提供了 1998 年 10 月到 2005 年 12 月间 480 189 个用户对 17 770 部电影做出的共 103 297 638 条评分记录。每条记录的取值为 1~5 的整数,分值越高代表用户对电影的评价越高,显然,如果每位用户都对所有电影都做了评价,则评分记录的总数将达到 853 2958 530 条,但是在实际的数据集中,评价记录只有该数字的 1.2% 。这也很好理解,一个用户一辈子可能也只能看几百部电影。在这个竞赛中,目标是根据已有的用户评分来对那些缺失的记录进行预测。在协同过滤中,可以采用矩阵分解来完成这个任务。

矩阵分解的基本思想是把原始用户商品矩阵 R 分解为两个小规模矩阵 UV,即

矩阵分解的结果就可以用来生成新的用户评分矩阵,也就是那些用户没有打分的电影的预测分值,从而将最高分值的电影推荐给对应的用户。矩阵分解模型有非常自然的解释,我们可以假设有若干个维度来对电影进行评价,如剧情、视觉效果、声音效果、音乐和剪辑等,每一个维度都有其分值。假设所有用户对电影的评价 R 都是对这些维度进行加权线性组合得到,即: R = w1 × 剧情 + w2 × 视觉效果 + w3 × 声音效果 + w4 × 音乐 + w5 × 剪辑 ,那么矩阵 V 的每一列都对应了一部电影在上述 5 个维度的分值,矩阵 U 中的每一列则对应了一位用户对这 5 个维度所赋予的权重,所以 VT U 矩阵就对应了所有用户对所有电影的评分,如下图所示:

V 中的每一列被称为电影特征向量, V 也被称为电影特征矩阵。 U 中的每一列被称为用户特征向量,其中用户商品矩阵的每个元素(代表了某个用户对某个电影的打分) rui 为:

其中 μ 是评分集合 R 中所有评分的均值, bu 是用户 u 的偏置, bi 是商品的偏置。模型参数 bi、bu、qi、pu 可以通过对下面这个损失函数分别求偏导,不断逼近到误差和的最小值,从而得到最优的参数组合:

损失函数的第一部分

表示误差和;

第二部分

表示正则化项。

这种方式把协同过滤转化为一个有监督学习的问题,被称为基于模型的协同过滤。用户矩阵 U 描述的是每个用户对于每个特征的偏好,我们可以理解为品味,这是完全主观的。而特征矩阵 V 对商品来说,可以理解为电影的风格,在这个场景中是客观的,这种方式有点类似于对品味和风格进行建模。得到了每个用户的品味和每个电影的风格后,我们就能按照下面的公式计算出所有用户对于所有商品的评分(补全数据集中缺失的部分),从而进行推荐:

Spark MLlib 中采用了基于模型的协同过滤。Spark MLlib 将传统协同过滤中用户商品矩阵中的值称为显式反馈,也就是用户对商品的明确评分,例如电影评分,而那些商品特征信息,如购买、转发、评论等称为隐式反馈。Spark MLlib 的矩阵分解模型是奇异值分解(SVD),采用交替最小二乘法(ALS)最小化误差。Spark 也提供了 SVD 的改良算法:SVD++算法的 GraphX 版本实现,SVD++ 在模型中加入了隐式反馈的影响。

Movielens 电影推荐系统

本课时将利用 Spark MLlib 实现一个推荐系统,如以下代码所示,使用的数据为 Movielens。Movielens 是一个关于电影评分的数据集,里面包含了多个用户对多部电影的评级数据,也包括电影元数据信息和用户属性信息。

import org.apache.spark.ml.Pipeline 
import org.apache.spark.ml.evaluation.RegressionEvaluator 
import org.apache.spark.ml.recommendation.ALS 
import org.apache.spark.sql.{RowSparkSession} 


object CollaborativeFilteringMovieLens { 

case class Rating(userId: Int, movieId: Int, rating: Float, timestamp: Long) 

  def main(args: Array[String]): Unit = { 

    val spark = SparkSession 
    .builder() 
    .master("local[2]") 
    .appName("CollaborativeFilteringMovieLens") 
    .enableHiveSupport() 
    .getOrCreate() 

    import spark.implicits._ 

    def parseRating(str: String): Rating = { 
      val fields = str.split("::") 
      assert(fields.size == 4) 
      Rating(fields(0).toInt, fields(1).toInt, fields(2).toFloat, fields(3).toLong) 
    } 

    val ratings = spark.read.textFile("data/mllib/rating").map(parseRating).toDF() 

    val Array(training, test) = ratings.randomSplit(Array(0.80.2)) 

    // 设置ALS算法参数 
    val als = new ALS() 
    .setMaxIter(5) 
    .setRegParam(0.01) 
    .setUserCol("userId") 
    .setItemCol("movieId") 
    .setRatingCol("rating") 
    .setColdStartStrategy("drop") 

    val pipeline = new Pipeline().setStages(Array(als)) 

    val model = pipeline.fit(training) 

    val predictions = model.transform(test) 

    // 设置模型评价指标 
    val evaluator = new RegressionEvaluator() 
    .setMetricName("rmse") 
    .setLabelCol("rating") 
    .setPredictionCol("prediction") 

    val rmse = evaluator.evaluate(predictions) 

    println(s"Root-mean-square error = $rmse") 

  } 
}

总结

本课时从原理出发,以协同过滤算法的进化为主线学习了推荐引擎。推荐引擎也可以算是最早以及最成功的大数据应用之一。

最后给大家留一个思考题:推荐引擎如何冷启动?


如何对模型性能进行评估并调优?

在开始今天的课程前,我们先来看看上个课时的习题:推荐引擎如何冷启动?冷启动是一个比较复杂也比较常见的问题。这里简单介绍下,可以选用的方法有用户登录自选标签,利用用户的注册信息分类并进行相应的推荐等。

而当我们 完成训练之后我们还需要对模型进行评估,根据评估的结果,有可能需要对模型超参进行优化,以达到理想的效果。本课时的主要内容有:

  • 模型评估

  • 交叉验证与超参调优

如果你是用 MLlib 来进行模型构建,那么本节内容的使用频率会大大高于本模块的其他内容。

模型评估

评估模型的质量,通常可以用一些评价方法说明,如精确率、召回率、AUC 、ROC 、PRC 等。我们先从混淆矩阵讲起,如果模型是一个二分类模型,当预测的概率值大于某个阈值,我们就将其归为类 1,那么我们可以从测试集的结果中得到如下表所示的矩阵。

上表中 True Positive 是本身为类 1,预测为类 1 的数目,称为真阳性;False Positive 是本身为类 0,预测为类 1 的数目,称为伪阳性;False Negative 是本身为类 1,预测为类 0 的数目,称为伪阴性;True Negative 是本身为类 0,预测为类 0 的数目,称为真阴性。根据这 4 个值,可以计算出精确率、召回率和 F 值。

  • 精确率 P= TP / (TP + FP)。精确率表示在预测为类 1 的样本中,实际为类 1 的样本比例。有一些模型对精确率要求很高,例如垃圾邮件监测,宁愿放过垃圾邮件也不愿误判一封正常邮件。

  • 召回率 R= TP / (TP + FN)。召回率表示在实际为类 1 的样本中,预测为类 1 的样本比例。有一些模型对召回率的要求很高,例如肿瘤监测,宁愿误判也不愿错过。

  • 准确率 = (TP + TN) / ALL。准确率表示正确预测的样本比例。

  • F=2/(1/P+1/R)=2PR/(P+R)。F 值是精确率与召回率的几何平均值,是对模型的一种综合评估,一个值比一条曲线来得直观。

  • ROC 与 PRC。ROC 曲线的横轴 FPR(False Positive Rate,伪阳性率)= FP/(FP + TN);纵轴 TPR(True Positive Rate,真阳性率)= TP/(TP + FN),曲线上的每个点代表不同的阈值所带来不同分类结果,阈值变化越细,ROC 曲线越平滑。如果存在一个理想的阈值,高于该阈值的都为类 1,反之为类 2,那么该模型就能完美区分两个类,此时横轴值为 0,纵轴值为 1,所以该 ROC 曲线经过 (0, 1) 点,曲线下面积为 1。通常我们用 ROC 曲线下面积(Area Under Curve,AUC)来评估模型质量,最大值为 1,越大说明模型效果越好,如下图所示中 3 条曲线分别代表了 3 个模型表现。

还有一种曲线叫精确召回率曲线(Precision Recall Curve,PRC),横轴是召回率,纵轴是精确率,与 ROC 越左凸效果越好不同,PRC 是越右凸效果越好,同样是曲线下面积越大越好。如下图所示,最上面的一条(AUC = 0.79)比最下面的一条(AUC = 0.39)效果好。PRC 可以在给定精确率或者召回率的情况下,去比较召回率或者精确率。

Spark MLlib 提供了模型评估的 API,我们在得到了测试结果后,可以使用如下代码对模型进行评估:

// 逻辑回归模型得到的测试结果 
val result = model.transform(test) 
val evaluator = new org.apache.spark.ml.evaluation.RegressionEvaluator 
// 指定模型评估指标 
var evaluatorParamMap = ParamMap(evaluator.metricName-> "areaUnderROC") 
var aucTraining = evaluator.evaluate(result, evaluatorParamMap)

交叉验证与超参调优

在机器学习中,我们都会将数据分为两份,一份作为训练集,另一份作为测试集,这就是交叉验证的思想。但在样本量不足的情况下,我们经常采用 k 折交叉验证法来充分测试,以期得到更好的效果。

k 折交叉验证法将全量数据分为 k 份,每次选一份作为测试集,剩下的作为训练集,如下图所示是一个10 折交叉验证。

k 折交叉验证通常和超参调优一起使用,超参是在开始训练过程之前设置值的参数,而不是通过训练得到的参数。例如,随机森林模型的超参数有:

  • 树的个数;

  • 树的最大深度;

  • 特征子集选取策略;

  • 每个特征分裂时,最大划分数量;

  • 纯度。

调整这些参数对模型性能有着巨大影响。通常,没有特别好的方法来指定这些参数,经验通常会很有用。我们只能通过不停地实验各种参数组合来测试模型性能,这样做的前提是,一定要保证组合非常全面。Spark MLlib 强大的运算能力能让我们快速测试大量的组合。

还是以随机森林模型为例,下面的代码可以让我们进行参数组合的测试:

val paramGrid = new ParamGridBuilder() 
.addGrid(rf.numTrees, 3 :: 5 :: 10 :: Nil) 
.addGrid(rf.featureSubsetStrategy, "auto" :: "all" :: Nil) 
.addGrid(rf.impurity, "gini" :: "entropy" :: Nil) 
.addGrid(rf.maxBins, 2 :: 5 :: Nil) 
.addGrid(rf.maxDepth, 3 :: 5 :: Nil) 
.build()

这里实际上我们定义了一个超参空间,空间的维度与超参的个数一样,所以 5 维空间中的每一个点代表了一个参数组合。下面我们创建一个交叉验证器,并定义 k = 5:

val crossValidator = new CrossValidator() 
.setEstimator(new Pipeline().setStages(Array(labelIndexer, featureIndexer, rf))) 
.setEstimatorParamMaps(paramGrid) 
.setNumFolds(5) 
.setEvaluator(evaluator)

并通过训练测试所有的参数组合从而得到最好的一组参数组合:

val crossValidatorModel = crossValidator.fit(data) 
// 得到效果最好的模型 
val bestModel = crossValidatorModel.bestModel 

val bestPipelineModel =crossValidatorModel.bestModel.asInstanceOf[PipelineModel]
val stages = bestPipelineModel.stages

// 得到最佳的模型超参
val rfStage = stages(stages.length-1).asInstanceOf[RandomForestClassificationModel]
val numTrees = rfStage.getNumTrees
val featureSubsetStrategy = rfStage.getFeatureSubsetStrategy
val impurity = rfStage.getImpurity
val maxBins = rfStage.getMaxBins
val maxDepth = rfStage.getMaxDepth

CrossValidator 继承了 Estimator 并实现了 fit() 方法。在 fit() 方法调用后,整个 Pipeline 会执行多次,包含从数据预处理到训练模型。在这个例子中,超参一共有 5 个,并进行了 5 折实验,需要执行 3 × 2 × 2 × 2 × 2 × 5 = 240 次 Pipeline,因此每多一个超参数,计算量通常都会大大增长,得益于 Spark 的并行计算能力,计算时间可以大大缩短。

总结

MLlib 不光为数据预处理与训练模型提供了解决方案,还为模型评估与调优提供了接口。如开头所述,不要看本课时的篇幅较短,但实际上使用率会很高。

本课时学完,Spark 所有组件就已经学完。其实大家不难发现,从模块三开始,使用场景是越来越窄的,批处理 -> 流处理 -> 图挖掘 -> 机器学习,所以本模块对于数据科学家来说在某些情况下是很有用的,但是对于数据工程师来说,使用场景并没有那么多。

最后给大家留一个思考题:树的最大深度对随机森林模型效果有什么影响呢?


数据仓库与商业智能系统架构剖析

在开始今天的课程前,先来看看上节课的思考题:树的最大深度对随机森林模型效果有什么影响呢?答案是树的最大深度可以用来控制模型的过拟合。

从这个模块开始,我们将进入到项目实战练习。在那之前,我们先来简要介绍下要实现的系统架构及相关的理论知识,这样在实现的过程中,大家能够更加清楚地领会整个过程,达到先务虚再务实、务虚是为了更好地务实的效果。

本课时的主要内容有:

  • 数据仓库

  • 数据立方体与多维分析

  • 商业智能系统

数据仓库

数据仓库的特征

按照数据仓库系统构造方面的领衔设计师 William H.Inmon 的说法,“数据仓库是一个面向主题的、集成的、时变的、非易失的数据集合,支持管理者的决策过程”,它指出了数据仓库的四个主要特征,下面我们分别进行解释。

  • 面向主题的(subject-oriented):数据仓库围绕一些重要的主题,如顾客、供应商、产品和销售组织。数据仓库关注决策者的数据建模和分析,而不是单位的日常操作和事物处理。因此数据仓库通常排除对于决策无用的数据,只提供特定主题的简明视图。

  • 集成的(integrated):通常构造数据仓库是将多个异构数据源,如关系数据库、一般文件和联机事务处理记录集成在一起。它使用数据清理和数据集成技术,以确保命名约定、编码结构、属性度量等的一致性。

  • 时变的(time-variant):数据仓库从历史的角度(例如,过去 5~10 年)提供信息。数据仓库中的关键结构都隐式或显式地包含时间元素。

  • 非易失的(nonvolatile):数据仓库总是物理地分离存放数据,这些数据源于操作环境下的应用数据。由于这种分离,数据仓库不需要事务处理、恢复和并发控制机制。数据的易失性在于操作型系统是一次访问和处理一条记录,可以对操作环境中的数据进行更新。但是数据仓库中的数据呈现出非常不同的特性,数据仓库中的数据通常是一次性加载,但在数据仓库环境中并不进行一般意义上的数据更新。通常,它只需要两种数据访问操作:数据的初始化加载和数据访问。

业务数据库与数据仓库的区别

操作数据库系统的主要任务是执行联机事务和查询处理,这种系统称作联机事务处理(OLTP)系统。它涵盖了组织的大部分日常操作,如购物、库存、工资等,也被称作业务系统。

数据仓库系统在数据分析和决策方面为用户提供服务,这种系统称作联机分析处理(OLAP)系统

两种系统的主要区别概述如下:

  • 用户和系统的面向性:OLTP 是面向客户的,用于办事员、客户和信息技术专业人员的事务和查询处理。OLAP 是面向市场的,用于知识工人(包括经理、主管和分析人员)的数据分析。

  • 数据内容:OLTP 系统管理的是当前数据,通常这种数据太琐碎,很难用于决策。OLAP 系统管理的则是大量的历史数据,提供汇总和聚集机制,并在不同的粒度层上存储和管理信息,这些特点使得数据更容易用于有根据的决策。

  • 视图:OLTP 系统主要关注一个企业或部门内部的当前数据,而不涉及历史数据或不同组织的数据。相比之下,由于组织的演变,OLAP 系统常常跨越数据库模式的多个版本。OLAP 系统还要处理来自不同组织的信息,以及由多个数据库集成的信息。由于数据量巨大,OLAP 系统的数据也存放在多个存储介质上。

  • 访问模式:OLTP 系统主要由短的原子事务组成,这种系统需要并发控制和恢复机制。然而,对 OLAP 系统的访问大部分是只读操作(由于大部分数据仓库存放历史数据,而不是最新数据),尽管许多访问可能是复杂的查询。

其他区别还包括数据库大小、操作的频繁程度、性能度量等,在这里就不再展开。

既然操作数据库存放了大量数据,你可能会奇怪“为什么不直接在这种数据库上进行联机分析处理(OLAP),而是另外花费时间和资源去构造分离的数据仓库?”。分离的主要原因是有助于提高两个系统的性能。操作数据库是为已知的任务和负载设计的,如使用的主键索引,检索特定的记录,优化“定制的”查询。而数据仓库的查询通常是复杂的,涉及大量数据汇总级的计算,可能需要特殊的基于多维视图的数据组织、存取方法和实现方法。在操作数据库上处理 OLAP 查询,可能会大大降低操作任务的性能。

此外,操作数据库支持多事务的并发处理,需要并发控制和恢复机制(例如,加锁和记日志),以确保一致性和事务的鲁棒性。通常,OLAP 查询只需要对汇总和聚集数据记录进行只读访问。如果将并发控制和恢复机制用于这种 OLAP 操作,就会危害并行事务的运行,从而大大降低 OLTP 系统的吞吐量。

最后,数据仓库与操作数据库分离是由于这两种系统中数据的结构、内容和用法都不相同。决策支持需要历史数据,而操作数据库一般不维护历史数据。在这种情况下,操作数据库中的数据尽管很丰富,但对于决策是远非完整的。决策支持需要整合来自异构源的数据(例如,聚集和汇总),产生高质量的、纯净的和集成的数据。操作数据库只维护详细的原始数据(如事务),这些数据在进行分析之前需要整理。由于两种系统提供大不相同的功能,需要不同类型的数据,因此需要维护分离的数据库。

数据集市

在数据仓库架构中,还存在一种形态,叫数据集市。数据集市往往服务于一组特定群体的分析需求(如会计部分或者信贷部门)。有些数据集市是独立的,也就是说它可以独立于数据仓库存在,而直接由业务数据库的历史应用创建。但更多的情况是,数据集市往往作为数据仓库之上的一个面向分析应用,换言之,数据集市的用户往往是直接和业务相关的分析应用。

数据立方体与多维分析

限于篇幅,这里就不和大家详细介绍数据仓库与数据集市建模的方法论,而是介绍一种数据结构和分析方法,在后面的项目中,你可以看到我们是如何将这种结构和方法融入项目中去的。

我们先来介绍什么是数据立方体。以下表为例,表中一共有四列,其中 sales_count 被称为度量,而其余三个字段被称为维度,整个这张表就可以看成是一个数据立方体,表格维度就可以看成立方体的维度。

sales_countsales_areasales_seasonsales_item
1223四川自行车
3244四川自行车
3242四川自行车
1555四川自行车
3333北京自行车
4444北京自行车
5555北京自行车
3333北京自行车
2312四川运动水壶
3233四川运动水壶
3222四川运动水壶
1110四川运动水壶
2323北京运动水壶
3243北京运动水壶
1121北京运动水壶
2343北京运动水壶

为了能够可视化,这里特意用了三个维度,数据立方体可以看成下面的坐标系:

观察数据会发现 sales_area 的值有两个:北京、四川;sales_season 的值有四个:春、夏、秋、冬;sales_item 的值有两个:运动水壶和自行车。我们可以将这些值看成坐标轴上的刻度,则这个大的立方体就可以被分割为 2 * 4 * 2 个子立方体。

基于数据立方体的维度,我们就可以进行多维分析,比如分析季节维度下的均值、总量等等。具体的实现则是用 SQL 的 GROUP BY 子句 + 对应的聚合函数完成,这其实也是多维分析中常见的上卷操作。

多维分析中常见的操作包括: 切片,切块,旋转,上卷,下钻。我们 以下面这个数据立方体为例,来看下它们分别指的是什么:

  • 切片(Slice)和切块(Dice):如下图所示,切片是在数据立方体的某一维度上选定一个维成员,而切块是对两个或多个维执行进行选择。

  • 旋转(Pivot):如下图所示,旋转指改变报表或页面的展示方向。对于使用者来说,就是个视图操作,而从 SQL 模拟语句的角度来说,就是改变 SELECT 后面字段的顺序而已。

  • 下钻(Drill-down)和上卷(Roll-up):下钻指将某些维度进行细分, 比如将省份维度下钻到城市维度,或是将时间维度从季度的粒度下钻到月份,如下图中将时间维度细分为 4 月、5 月、6 月。上卷可以理解为"无视"某些维度,比如只基于省份和品类进行聚合分析,如下图所示。

基于数据仓库的多维分析通常广泛运用在商业智能系统中,下面我们来看看商业智能系统。

商业智能系统

哪里有数据,哪里就有数据挖掘应用,这句话用来形容商业智能再合适不过了。数据仓库解决了存储问题,而 OLAP 技术提供了挖掘手段,企业自然而然会想到将数据利用起来,商业智能就是最好的途径。

**商业智能(Business Intelligence,BI)**是一个统称,指的是用于支持制定业务决策的技能、流程、技术、应用和实践。商业智能是将企业中现有的数据转化为知识,以帮助企业做出明智的业务经营决策的工具。具体来说,它通过对商业信息进行搜集、管理和分析,旨在使企业的各级决策者获得知识或洞察力(Insight),从而促使他们做出对企业更有利的决策。从技术层面上讲,商业智能不是什么新技术,它只是数据仓库、OLAP 等技术的综合运用。

大多数的数据仓库是为了挖掘某种商业价值而创建的,但是商业智能和数据仓库之间的区别在于,商业智能的定位是生成可向业务用户交付的产品,而数据仓库只对数据进行结构化的存储和组织,所以数据仓库需要 OLAP 技术,才能向商业智能转换。

下图演示了商业智能系统、数据仓库和 OLAP 的关系:

可以看出,商业智能系统通过对数据仓库的数据进行数据选择、抽取、加载后,使用数据挖掘方法提取知识,再用 BI 报表将知识呈现给决策者供其参考。

一款优秀的商业智能系统应该满足以下四个特性:准确及时价值高可操作。准确性的意义是数据是可信的,及时性意味着数据可定期获取,价值高表示对商业用户有用,可操作性是指信息可以用于业务决策过程。

总结

本课时详细讲解了后续实战项目中会用到的概念,但是并没有面面俱到,还有星形模型、雪花模型、数据仓库建模方法论等概念,详细讲解需要太多的理论延伸,对于我们后面的项目来说没有必要,但如果你有兴趣,可以多钻研。

不难看出,由于数据集市需要面向业务并且实时分析,数据立方体的结构非常适合它,而商业智能系统的中心正是能够支持 OLAP 分析、报表查询的数据集市。在后面我们会按照这种思路深入实战学习,本课时中最重要的是理解数据立方体和多维分析方法,这也是本节课的课后思考题


作为 Yelp 运营负责人,如何根据数据进行决策?

在本模块中,我为你设计了一个基于真实数据的项目,也就是前面提到的商业智能系统,可以让你对 Spark 有更形象的认识,并对前面学习的知识进行巩固。为了便于理解,该模块简化了业务复杂度和技术复杂度,作为你的第一个 Spark 项目是比较合适的。

本课时的主要内容是:

  • 美国的大众点评网 Yelp 和 Yelp 2016 Dataset Challenge 数据集介绍。

  • 作为 Yelp 运营负责人,你希望知道什么?

Yelp 与数据集介绍

Yelp 由前 PayPal 员工罗素·西蒙斯和杰里米·斯托普尔曼于 2004 年在美国旧金山成立,公司发展迅速,得到几轮融资支持,2010 年营业额达到 3 千万美元,以及 450 万评论量。2009 年到 2012 年,其在欧洲和亚洲的业务也蓬勃发展。

这是一个著名的商户点评网站,与我们的大众点评网类似,美国各地用户只要注册就能在 Yelp 网站上给商户打分、评论,还能和其他用户交流感想体验。Yelp 上的商户包括各地餐馆、购物中心、酒店、旅游等。

就像 Yelp 的 slogan 一样,这家公司看重的是真实客户的真实评价,尤其注重把一小部分热衷点评的用户吸引过来,同时给予优质客户奖励。这种形式提高了 Yelp 网站上各种信息的可信度,也把有深入体验的优质用户和那些走马观花的用户区分开了。

Yelp 在 2016 年公开了其内部业务数据集(Yelp 2016 Dataset Challenge),供数据科学竞赛爱好者使用,Kaggle 也收录了该数据集(下载地址:https://www.kaggle/yelp-dataset/yelp-dataset)。由于是真实数据集,其中有不少有趣的内容,我们这次的项目也是基于该数据集。

下面我们对数据集进行一个介绍,它包括以下几个表:

  • 业务表 (businesss)

  • 评价表 (reviews)

  • 小贴士表 (tips)

  • 用户信息表 (user information)

  • 签到表 (check-ins)

数据集共 10G 左右,包含 668 万条评论。它来自 19 万个商业机构,涵盖了 10 个都市区域,有大概 100 多万个属性标签。此外,Yelp 还提供了一些图像数据,比如商户的图片数据,由于在本项目中用不到,故不对其进行介绍。

business 表

以下代码是以 Json 格式表示的 business 表中的一条数据,代表了一个商业组织的相关数据(例如某个餐馆)。

{
    // string, 22 character unique string business id
    "business_id": "tnhfDv5Il8EaGSXZGiuQGg",
    // string, the business's name
    "name": "Garaje",
    // string, the full address of the business
    "address": "475 3rd St",
    // string, the city
    "city": "San Francisco",
    // string, 2 character state code, if applicable
    "state": "CA",
    // string, the postal code
    "postal code": "94107",
    // float, latitude
    "latitude": 37.7817529521,
    // float, longitude
    "longitude": -122.39612197,
    // float, star rating, rounded to half-stars
    "stars": 4.5,
    // integer, number of reviews
    "review_count": 1198,
    // integer, 0 or 1 for closed or open, respectively
    "is_open": 1,
    // object, business attributes to values. note: some attribute values might be objects
    "attributes": {
        "RestaurantsTakeOut": true,
        "BusinessParking": {
            "garage": false,
            "street": true,
            "validated": false,
            "lot": false,
            "valet": false
        },
    },
    // an array of strings of business categories
    "categories": [
        "Mexican",
        "Burgers",
        "Gastropubs"
    ],
    // an object of key day to value hours, hours are using a 24hr clock
    "hours": {
        "Monday": "10:00-21:00",
        "Tuesday": "10:00-21:00",
        "Friday": "10:00-21:00",
        "Wednesday": "10:00-21:00",
        "Thursday": "10:00-21:00",
        "Sunday": "11:00-18:00",
        "Saturday": "10:00-21:00"
    }
}

为了让你对这些字段有更直观的认识,我截取了 Yelp 网站商家主页的部分内容,如下图所示,网页内容可以很容易地和上述数据对应。


结合网页内容,我们可以更形象地观察上面的代码,它包含商家的开业时间、评论条数、地理位置,以及属性值与类目信息等。

review 表

以下代码是以 Json 格式表示的 review 表中的一条数据,代表了一个用户(user)对一个商业机构(busniess)的一条评论。

{
    // string, 22 character unique review id
    "review_id": "zdSx_SD6obEhz9VrW9uAWA",
    // string, 22 character unique user id, maps to the user in user.json
    "user_id": "Ha3iJu77CxlrFm-vQRs_8g",
    // string, 22 character business id, maps to business in business.json
    "business_id": "tnhfDv5Il8EaGSXZGiuQGg",
    // integer, star rating
    "stars": 4,
    // string, date formatted YYYY-MM-DD
    "date": "2016-03-09",
    // string, the review itself
    "text": "Great place to hang out after work: the prices are decent, and the ambience is fun. It's a bit loud, but very lively. The staff is friendly, and the food is good. They have a good selection of drinks.",
    // integer, number of useful votes received
    "useful": 0,
    // integer, number of funny votes received
    "funny": 0,
    // integer, number of cool votes received
    "cool": 0
}

结合网页中与评论相关的内容进行观察,如下图所示:

评论表的一条数据包含完整的评论文本数据,包括撰写评论的 user_id 和撰写评论的对象 business_id。结合网页,同样会让你有更形象的感知。

user 表

以下代码是以 Json 格式表示的 user 表中的一条数据,代表了一个用户的基本情况。

{
    // string, 22 character unique user id, maps to the user in user.json
    "user_id": "Ha3iJu77CxlrFm-vQRs_8g",
    // string, the user's first name
    "name": "Sebastien",
    // integer, the number of reviews they've written
    "review_count": 56,
    // string, when the user joined Yelp, formatted like YYYY-MM-DD
    "yelping_since": "2011-01-01",
    // array of strings, an array of the user's friend as user_ids
    "friends": [
        "wqoXYLWmpkEH0YvTmHBsJQ",
        "KUXLLiJGrjtSsapmxmpvTA",
        "6e9rJKQC3n0RSKyHLViL-Q"
    ],
    // integer, number of useful votes sent by the user
    "useful": 21,
    // integer, number of funny votes sent by the user
    "funny": 88,
    // integer, number of cool votes sent by the user
    "cool": 15,
    // integer, number of fans the user has
    "fans": 1032,
    // array of integers, the years the user was elite
    "elite": [
        2012,
        2013
    ],
    // float, average rating of all reviews
    "average_stars": 4.31,
    // integer, number of hot compliments received by the user
    "compliment_hot": 339,
    // integer, number of more compliments received by the user
    "compliment_more": 668,
    // integer, number of profile compliments received by the user
    "compliment_profile": 42,
    // integer, number of cute compliments received by the user
    "compliment_cute": 62,
    // integer, number of list compliments received by the user
    "compliment_list": 37,
    // integer, number of note compliments received by the user
    "compliment_note": 356,
    // integer, number of plain compliments received by the user
    "compliment_plain": 68,
    // integer, number of cool compliments received by the user
    "compliment_cool": 91,
    // integer, number of funny compliments received by the user
    "compliment_funny": 99,
    // integer, number of writer compliments received by the user
    "compliment_writer": 95,
    // integer, number of photo compliments received by the user
    "compliment_photos": 50
}

而与数据对应的网站用户主页部分如下图所示:

一条用户数据包括该用户的朋友以及与该用户关联的所有元数据。

tip 表

以下代码是以 Json 格式表示的 tip 表中的一条数据,代表了一条简短的评论。

{
    // string, text of the tip
    "text": "Secret menu - fried chicken sando is da bombbbbbb Their zapatos are good too.",
    // string, when the tip was written, formatted like YYYY-MM-DD
    "date": "2013-09-20",
    // integer, how many compliments it has
    "compliment_count": 172,
    // string, 22 character business id, maps to business in business.json
    "business_id": "tnhfDv5Il8EaGSXZGiuQGg",
    // string, 22 character unique user id, maps to the user in user.json
    "user_id": "49JhAJh8vSQ-vM4Aourl0g"
}

与数据对应的网站小贴士页如下图所示:

这种小贴士的对象同样是商家,但小贴士比评论更简短,并且倾向于传达快速的建议。

checkin 表

以下代码是以 Json 格式表示的 checkin 表中的一条数据,代表了一个商户的签到情况,包括商户的 business_id 及顾客的签到时间。

{
    // string, 22 character business id, maps to business in business.json
    "business_id": "tnhfDv5Il8EaGSXZGiuQGg"
    // string which is a comma-separated list of timestamps for each checkin, each with format YYYY-MM-DD HH:MM:SS
    "date": "2016-04-26 19:49:16, 2016-08-30 18:36:57, 2016-10-15 02:45:18, 2016-11-18 01:54:50, 2017-04-20 18:39:06, 2017-05-03 17:58:02"
}

从 Yelp 运营负责人的视角分析

高质量的评论是 Yelp 的灵魂,作为 Yelp 的运营负责人,你希望能从评论中发现些有趣的东西,从而更好地帮助商家,活跃用户。

从数据方体的角度来看,review 表中 stars、useful、funny、cool 字段就是度量,而 busniess 表中的 city、state 字段以及 review 表中的 date 字段都可以作为时间与空间维度。通过数据方体与多维分析,能够很好地对数据进行分析。

Yelp 的商业智能系统希望构建一个关于评论的数据方体,并进行相应的分析与可视化。通过这种方法,将得到以商业机构为主体的相关“业务知识”,以辅助运营人员更好地决策。例如评分更高的商业主体有哪些特征,以及有哪些特征是我们平时所忽略的,等等。详细方法在之后的课程会展开。

这里注意,为了简化,本项目只会构建一个数据方体,但是在一般规模的生产环境中,商业智能系统需要构建的数据方体的数量非常多,基础数据也远远不止我们这个项目中的 5 张表。

需要特别说明的一点是,通常在数据仓库或者商业智能系统项目开始时,你并不会直接获取到上面下载的 Yelp 的数据集,我们需要的数据往往在业务数据库中,所以下个课时,我们将模拟将数据从业务数据库中导出的过程。

总结

作为项目开始的第一个课时,我主要带你熟悉数据集、数据结构,了解商业智能系统的分析需求。为了便于理解,无论是对数据集还是分析需求都做了大量简化。如果是真实情况,每个环节都会增加上百倍的复杂性。所以近期课程都需要你反复巩固,以便更好地运用到工作中。


本文标签: 数据实战第八集Spark