========== =========
8.1.1 什么是机器学习
机器学习可以看做是一门人工智能的科学,该领域的主要研究对象是人工智能。机器学习利用数据或以往的经验,以此优化计算机程序的性能标准。
机器学习强调三个关键词:算法、经验、性能
从最小二乘法说起
机器学习的泛化能力
机器学习的过拟合问题
8.1.2 基于大数据的机器学习
8.1.3 Spark 机器学习库MLLib
Spark 机器学习库从1.2 版本以后被分为两个包:
8.2 机器学习工作流
8.2.1 机器学习工作流概念
在介绍工作流之前,先来了解几个重要概念:
工作流的各个阶段按顺序运行,输入的DataFrame在它通过每个阶段时被转换
本节以逻辑斯蒂回归为例,构建一个典型的机器学习过程,来具体介绍一下工作流是如何应用的
任务描述 查找出所有包含"spark"的句子,即将包含"spark"的句子的标签设为1,没有"spark"的句子的标签设为0。
8.2.2 构建一个机器学习工作流
(1)引入要包含的包并构建训练数据集
(2)定义 Pipeline 中的各个工作流阶段PipelineStage,包括转换器和评估器,具体地,包含tokenizer, hashingTF和lr。
(3)按照具体的处理逻辑有序地组织PipelineStages,并创建一个Pipeline。
现在构建的Pipeline本质上是一个Estimator,在它的fit()方法运行之后,它将产生一个PipelineModel,它是一个Transformer。
可以看到,model的类型是一个PipelineModel,这个工作流模型将在测试数据的时候使用
(4)构建测试数据
(5)调用之前训练好的PipelineModel的transform()方法,让测试数据按顺序通过拟合的工作流,生成预测结果
8.3 特征抽取、转化和选择
8.3.1 特征抽取:TF-IDF
“词频-逆向文件频率”(TF-IDF)是一种在文本挖掘中广泛使用的特征向量化方法,它可以体现一个文档中词语在语料库中的重要程度。
词语由t表示,文档由d表示,语料库由D表示。词频TF(t,d)是词语t在文档d中出现的次数。文件频率DF(t,D)是包含词语的文档的个数。
TF-IDF就是在数值化文档信息,衡量词语能提供多少信息以区分文档。其定义如下:
TF-IDF 度量值表示如下:
TF: HashingTF 是一个Transformer,在文本处理中,接收词条的集合然后把这些集合转化成固定长度的特征向量。这个算法在哈希的同时会统计各个词条的词频。
IDF: IDF是一个Estimator,在一个数据集上应用它的fit()方法,产生一个IDFModel。 该IDFModel 接收特征向量(由HashingTF产生),然后计算每一个词在文档中出现的频次。IDF会减少那些在语料库中出现频率较高的词的权重。
(1)导入TF-IDF所需要的包
开启RDD的隐式转换
(2)创建一个简单的DataFrame,每一个句子代表一个文档
(3)得到文档集合后,即可用tokenizer对句子进行分词
(4)得到分词后的文档序列后,即可使用HashingTF的transform()方法把句子哈希成特征向量,这里设置哈希表的桶数为2000
(5)使用IDF来对单纯的词频特征向量进行修正,使其更能体现不同词汇对文本的区别能力
IDF是一个Estimator,调用fit()方法并将词频向量传入,即产生一个IDFModel
IDFModel是一个Transformer,调用它的transform()方法,即可得到每一个单词对应的TF-IDF度量值
8.3.2 特征抽取:Word2Vec
Word2Vec是一种著名的词嵌入(Word Embedding)方法,它可以计算每个单词在其给定语料库环境下的分布式词向量
词向量表示可以在一定程度上刻画每个单词的语义 如果词的语义相近,它们的词向量在向量空间中也相互接近
Word2vec是一个Estimator,它采用一系列代表文档的词语来训练word2vecmodel 该模型将每个词语映射到一个固定大小的向量
word2vecmodel使用文档中每个词语的平均数来将文档转换为向量,然后这个向量可以作为预测的特征,来计算文档相似度计算等等
任务描述: 一组文档,其中一个词语序列代表一个文档。对于每一个文档,我们将其转换为一个特征向量。此特征向量可以被传递到一个学习算法。
(1)首先导入Word2Vec所需要的包,并创建三个词语序列,每个代表一个文档:
(2)新建一个Word2Vec,显然,它是一个Estimator,设置相应的超参数,这里设置特征向量的维度为3
(3)读入训练数据,用fit()方法生成一个Word2VecModel
(4)利用Word2VecModel把文档转变成特征向量
8.3.3 特征抽取:CountVectorizer
CountVectorizer旨在通过计数来将一个文档转换为向量 当不存在先验字典时,Countvectorizer作为Estimator提取词汇进行训练,并生成一个CountVectorizerModel用于存储相应的词汇向量空间
该模型产生文档关于词语的稀疏表示,其表示可以传递给其他算法,例如LDA
在CountVectorizerModel的训练过程中,CountVectorizer将根据语料库中的词频排序从高到低进行选择,词汇表的最大含量由vocabsize超参数来指定,超参数minDF,则指定词汇表中的词语至少要在多少个不同文档中出现
(1)首先导入CountVectorizer所需要的包:
(2)假设有如下的DataFrame,其包含id和words两列,可以看成是一个包含两个文档的迷你语料库
(3)通过CountVectorizer设定超参数,训练一个CountVectorizerModel,这里设定词汇表的最大量为3,设定词汇表中的词至少要在2个文档中出现过,以过滤那些偶然出现的词汇
(4)在训练结束后,可以通过CountVectorizerModel的vocabulary成员获得到模型的词汇表
从打印结果我们可以看到,词汇表中有“a”,“b”,“c”三个词,且这三个词都在2个文档中出现过(前文设定了minDF为2)
(5)使用这一模型对DataFrame进行变换,可以得到文档的向量化表示:
和其他Transformer不同,CountVectorizerModel可以通过指定一个先验词汇表来直接生成,如以下例子,直接指定词汇表的成员是“a”,“b”,“c”三个词:
8.3.4 特征变换:标签和索引的转化
在机器学习处理过程中,为了方便相关算法的实现,经常需要把标签数据(一般是字符串)转化成整数索引,或是在计算结束后将整数索引还原为相应的标签
Spark ML包中提供了几个相关的转换器,例如:StringIndexer、IndexToString、OneHotEncoder、VectorIndexer,它们提供了十分方便的特征转换功能,这些转换器类都位于org.apache.spark.ml.feature包下 值得注意的是,用于特征转换的转换器和其他的机器学习算法一样,也属于ML Pipeline模型的一部分,可以用来构成机器学习流水线,以StringIndexer为例,其存储着进行标签数值化过程的相关 超参数,是一个Estimator,对其调用fit(..)方法即可生成相应的模型StringIndexerModel类,很显然,它存储了用于DataFrame进行相关处理的 参数,是一个Transformer(其他转换器也是同一原理)
StringIndexer
StringIndexer转换器可以把一列类别型的特征(或标签)进行编码,使其数值化,索引的范围从0开始,该过程可以使得相应的特征索引化,使得某些无法接受类别型特征的算法可以使用,并提高诸如决策树等机器学习算法的效率
索引构建的顺序为标签的频率,优先编码频率较大的标签,所以出现频率最高的标签为0号
如果输入的是数值型的,我们会把它转化成字符型,然后再对其进行编码
(1)首先引入必要的包,并创建一个简单的DataFrame,它只包含一个id列和一个标签列category
(2)随后,我们创建一个StringIndexer对象,设定输入输出列名,其余参数采用默认值,并对这个DataFrame进行训练,产生StringIndexerModel对象:
(3)随后即可利用该对象对DataFrame进行转换操作,可以看到,StringIndexerModel依次按照出现频率的高低,把字符标签进行了排序,即出现最多的“a”被编号成0,“c”为1,出现最少的“b”为0
IndexToString
与StringIndexer相对应,IndexToString的作用是把标签索引的一列重新映射回原有的字符型标签
其主要使用场景一般都是和StringIndexer配合,先用StringIndexer将标签转化成标签索引,进行模型训练,然后在预测标签的时候再把标签索引转化成原有的字符标签。当然,你也可以另外定义其他的标签
(1)首先,和StringIndexer的实验相同,我们用StringIndexer读取数据集中的“category”列,把字符型标签转化成标签索引,然后输出到“categoryIndex”列上,构建出新的DataFrame
(2)随后,创建IndexToString对象,读取“categoryIndex”上的标签索引,获得原有数据集的字符型标签,然后再输出到“originalCategory”列上。最后,通过输出“originalCategory”列,可以看到数据集中原有的字符标签
OneHotEncoder
独热编码(One-Hot Encoding) 是指把一列类别性特征(或称名词性特征,nominal/categorical features)映射成一系列的二元连续特征的过程,原有的类别性特征有几种可能取值,这一特征就会被映射成几个二元连续特征,每一个特征代表一种取值,若该样本表现出该特征,则取1,否则取0
One-Hot编码适合一些期望类别特征为连续特征的算法,比如说逻辑斯蒂回归等。
(1)首先创建一个DataFrame,其包含一列类别性特征,需要注意的是,在使用OneHotEncoder进行转换前,DataFrame需要先使用StringIndexer将原始标签数值化:
(2)随后,我们创建OneHotEncoder对象对处理后的DataFrame进行编码,可以看见,编码后的二进制特征呈稀疏向量形式,与StringIndexer编码的顺序相同,需注意的是最后一个Category("b")被编码为全0向量,若希望"b"也占有一个二进制特征,则可在创建OneHotEncoder时指定setDropLast(false)
VectorIndexer
之前介绍的StringIndexer是针对单个类别型特征进行转换,倘若所有特征都已经被组织在一个向量中,又想对其中某些单个分量进行处理时,Spark ML提供了VectorIndexer类来解决向量数据集中的类别性特征转换
通过为其提供maxCategories超参数,它可以自动识别哪些特征是类别型的,并且将原始值转换为类别索引。它基于不同特征值的数量来识别哪些特征需要被类别化,那些取值可能性最多不超过maxCategories的特征需要会被认为是类别型的
在下面的例子中,我们读入一个数据集,然后使用VectorIndexer训练出模型,来决定哪些特征需要被作为类别特征,将类别特征转换为索引,这里设置maxCategories为2,即只有种类小于2的特征才被认为是类别型特征,否则被认为是连续型特征:
可以通过VectorIndexerModel的categoryMaps成员来获得被转换的特征及其映射,这里可以看到共有两个特征被转换,分别是0号和2号
可以看到,0号特征只有-1,0两种取值,分别被映射成0,1,而2号特征只有1种取值,被映射成0
特征选择(Feature Selection)指的是在特征向量中选择出那些“优秀”的特征,组成新的、更“精简”的特征向量的过程。它在高维数据分析中十分常用,可以剔除掉“冗余”和“无关”的特征,提升学习器的性能
特征选择方法和分类方法一样,也主要分为有监督(Supervised)和无监督(Unsupervised)两种 卡方选择则是统计学上常用的一种有监督特征选择方法,它通过对特征和真实标签之间进行卡方检验,来判断该特征和真实标签的关联程度,进而确定是否对其进行选择
和ML库中的大多数学习方法一样,ML中的卡方选择也是以estimator+transformer的形式出现的,其主要由ChiSqSelector和ChiSqSelectorModel两个类来实现
(1)在进行实验前,首先进行环境的设置。引入卡方选择器所需要使用的类:
(2)随后,创造实验数据,这是一个具有三个样本,四个特征维度的数据集,标签有1,0两种,我们将在此数据集上进行卡方选择:
(3)现在,用卡方选择进行特征选择器的训练,为了观察地更明显,我们设置只选择和标签关联性最强的一个特征(可以通过setNumTopFeatures(..)方法进行设置):
(4)用训练出的模型对原数据集进行处理,可以看见,第三列特征被选出作为最有用的特征列:
8.4 分类与回归
8.4.1 逻辑斯蒂回归分类器
逻辑斯蒂回归(logistic regression)是统计学习中的经典分类方法,属于对数线性模型。logistic回归的因变量可以是二分类的,也可以是多分类的。
8.4.1.1 用二项逻辑斯蒂回归来解决二分类问题
首先我们先取其中的后两类数据,用二项逻辑斯蒂回归进行二分类分析
2. 读取数据,简要分析
因为我们现在处理的是2分类问题,所以我们不需要全部的3类数据,我们要从中选出两类的数据 首先把刚刚得到的数据注册成一个表iris,注册成这个表之后,我们就可以通过sql语句进行数据查询
3. 构建ML的pipeline
(1)分别获取标签列和特征列,进行索引,并进行了重命名
(2)接下来,我们把数据集随机分成训练集和测试集,其中训练集占70%
(3)然后,我们设置logistic的参数,这里我们统一用setter的方法来设置,也可以用ParamMap来设置(具体的可以查看spark mllib的官网)。这里我们设置了循环次数为10次,正则化项为0.3等
(4)这里我们设置一个labelConverter,目的是把预测的类别重新转化成字符型的
(5)构建pipeline,设置stage,然后调用fit()来训练模型
(6)pipeline本质上是一个Estimator,当pipeline调用fit()的时候就产生了一个PipelineModel,本质上是一个Transformer。然后这个PipelineModel就可以调用transform()来进行预测,生成一个新的DataFrame,即利用训练得到的模型对测试集进行验证
(7)最后我们可以输出预测的结果,其中select选择要输出的列,collect获取所有行的数据,用foreach把每行打印出来。其中打印出来的值依次分别代表该行数据的真实分类和特征值、预测属于不同分类的概率、预测的分类
4. 模型评估
创建一个MulticlassClassificationEvaluator实例,用setter方法把预测分类的列名和真实分类的列名进行设置;然后计算预测准确率和错误率
从上面可以看到预测的准确性达到100%
接下来我们可以通过model来获取我们训练得到的逻辑斯蒂模型。前面已经说过model是一个PipelineModel,因此我们可以通过调用它的stages来获取模型,具体如下:
8.4.2 决策树分类器
决策树(decision tree)是一种基本的分类与回归方法,这里主要介绍用于分类的决策树。决策树模式呈树形结构,其中每个内部节点表示一个属性上的测试,每个分支代表一个测试输出,每个叶节点代表一种类别。学习时利用训练数据,根据损失函数最小化的原则建立决策树模型;预测时,对新的数据,利用决策树模型进行分类
决策树学习通常包括3个步骤:特征选择、决策树的生成和决策树的剪枝
(一)特征选择 特征选择在于选取对训练数据具有分类能力的特征,这样可以提高决策树学习的效率。通常特征选择的准则是信息增益(或信息增益比、基尼指数等),每次计算每个特征的信息增益,并比较它们的大小,选择信息增益最大(信息增益比最大、基尼指数最小)的特征
(二)决策树的生成
(三)决策树的剪枝
决策树生成算法递归地产生决策树,直到不能继续下去为止。这样产生的树往往对训练数据的分类很准确,但对未知的测试数据的分类却没有那么准确,即出现过拟合现象。解决这个问题的办法是考虑决策树的复杂度,对已生成的决策树进行简化,这个过程称为剪枝。
我们以iris数据集(iris)为例进行分析(iris下载地址:http://dblab.xmu.edu.cn/blog/wp-content/uploads/2017/03/iris.txt) iris以鸢尾花的特征作为数据来源,数据集包含150个数据集,分为3类,每类50个数据,每个数据包含4个属性,是在数据挖掘、数据分类中非常常用的测试集、训练集。
1. 导入需要的包
2. 读取数据,简要分析
3. 进一步处理特征和标签,以及数据分组
4. 构建决策树分类模型
5. 评估决策树分类模型
6. 构建决策树回归模型
7. 评估决策树回归模型
从上述结果可以看到模型的标准误差为 0.3676073110469039以及训练的决策树模型结构
8.5 聚类算法
K=2示意图
8.5 聚类算法
ML包下的KMeans方法位于org.apache.spark.ml.clustering包下,其过程大致如下:
1.根据给定的k值,选取k个样本点作为初始划分中心
2.计算所有样本点到每一个划分中心的距离,并将所有样本点划分到距离最近的划分中心
3.计算每个划分中样本点的平均值,将其作为新的中心; 循环进行2~3步直至达到最大迭代次数,或划分中心的变化小于某一预定义阈值
数据集:使用UCI数据集中的鸢尾花数据Iris进行实验,它可以在iris获取,Iris数据的样本容量为150,有四个实数值的特征,分别代表花朵四个部位的尺寸,以及该样本对应鸢尾花的亚种类型(共有3种亚种类型)
在使用前,引入需要的包:
开启RDD的隐式转换:
为了便于生成相应的DataFrame,这里定义一个名为model_instance的case class作为DataFrame每一行(一个数据样本)的数据类型
在定义数据类型完成后,即可将数据读入RDD[model_instance]的结构中,并通过RDD的隐式转换.toDF()方法完成RDD到DataFrame的转换:
在得到数据后,我们即可通过ML包的固有流程:创建Estimator并调用其fit()方法来生成相应的Transformer对象,很显然,在这里KMeans类是Estimator,而用于保存训练后模型的KMeansModel类则属于Transformer
与MLlib中的实现不同,KMeansModel作为一个Transformer,不再提供predict()样式的方法,而是提供了一致性的transform()方法,用于将存储在DataFrame中的给定数据集进行整体处理,生成带有预测簇标签的数据集
为了方便观察,我们可以使用collect()方法,该方法将DataFrame中所有的数据组织成一个Array对象进行返回:
也可以通过KMeansModel类自带的clusterCenters属性获取到模型的所有聚类中心情况:
与MLlib下的实现相同,KMeansModel类也提供了计算 集合内误差平方和(Within Set Sum of Squared Error, WSSSE) 的方法来度量聚类的有效性,在真实K值未知的情况下,该值的变化可以作为选取合适K值的一个重要参考:
8.6 推荐算法
推荐算法是计算机专业中的一种算法,通过一些数学算法,推测出用户可能喜欢的东西。
1、基于内容的信息推荐方法的理论依据主要来自于信息检索和信息过滤。根据用户过去的浏览记录来向用户推荐用户没有接触过的推荐项。
2、基于协同过滤的推荐算法理论上可以推荐世界上的任何一种东西。图片、音乐、样样可以。 协同过滤算法主要是通过对未评分项进行评分 预测来实现的。
3、基于关联规则的推荐(Association Rule-based Recommendation)是以关联规则为基础,把已购商品作为规则头,规则体为推荐对象。
8.6.1 协同过滤
关于协同过滤的一个经典的例子就是看电影。如果你不知道哪一部电影是自己喜欢的或者评分比较高的,那么通常的做法就是问问周围的朋友,看看最近有什么好的电影推荐。
协同过滤算法主要分为基于用户的协同过滤算法和基于项目的协同过滤算法。MLlib当前支持基于模型的协同过滤,其中用户和商品通过一小组隐语义因子进行表达,并且这些因子也用于预测缺失的元素。
Spark MLlib实现了 交替最小二乘法 (ALS) 来学习这些隐性语义因子。
8.6.1 显现和隐性反馈
8.6.2 ALS算法
8.7 超参数调优
8.7.1 超参数调优的原理
8.7.2 使用交叉验证进行模型选择
读取Irisi数据集,分别获取标签列和特征列,进行索引、重命名,并设置机器学习工作流。
使用ParamGridBuilder方便构造参数网格。
其中regParam参数定义规范化项的权重;elasticNetParam是Elastic net 参数,取值介于0和1之间。elasticNetParam设置2个值,regParam设置3个值。最终将有(3 * 2) = 6个不同的模型将被训练。
再接下来,构建针对整个机器学习工作流的交叉验证类,定义验证模型、参数网格,以及数据集的折叠数,并调用fit方法进行模型训练。 其中,对于回归问题评估器可选择RegressionEvaluator,二值数据可选择BinaryClassificationEvaluator,多分类问题可选择MulticlassClassificationEvaluator。评估器里默认的评估准则可通过setMetricName方法重写。
还可以获取最优的逻辑斯蒂回归模型,并查看其具体的参数. 对于参数网格,其最优参数取值是regParam=0.01,elasticNetParam=0.2。