年度归档:2018年


本章数据集下载

第11章 PySpark 决策树模型

Spark不但好用、而且还易用、通用,它提供多种的开发语言的API,除了Scala外,还有Java、Python、R等,可以说集成目前市场最有代表性的开发语言,使得Spark受众上升几个数据量级,同时也无形中降低了学习和使用它的门槛,使得很多熟悉Java、Python、R的编程人员、数据分析师,也可方便地利用Spark大数据计算框架来实现他们的大数据处理、机器学习等任务。
Python作为机器学习中的利器,一直被很多开发者和学习者所推崇的一种语言。除了开源、易学以及简洁的代码风格的特性之外,Python当中还有很多优秀的第三方的库,为我们对数据进行处理、探索和模型的构建提供很大的便利,如Pandas、Numpy、Scipy、Matplotlib、StatsModels、Scikit-Learn、Keras等。Python的强大还体现在它的与时俱进,它与大数据计算平台Spark的结合,可为是强强联合、优势互补、相得益彰,这就有了现如今Spark当中一个重要分支--PySpark。其内部架构可参考图11-1(该图取自https://cwiki.apache.org/confluence/display/SPARK/PySpark+Internals?spm=5176.100239.0.0.eI85ij)。

图11-1 PySpark架构图

PySpark的Python解释器在启动时会同时启动一个JVM,Python解释器与JVM进程通过socket进行通信,在python driver端,SparkContext利用Py4J启动一个JVM并产生一个JavaSparkContext。Py4J只使用在driver端,用于本地python与Java SparkContext objects的通信。大量数据的传输使用的是另一个机制。RDD在python下的转换会被映射成java环境下PythonRDD。在远端worker机器上,PythonRDD对象启动一些子进程并通过pipes与这些子进程通信,以此send用户代码和数据。
本章节就机器学习中的决策树模型,使用PySpark中的ML库以及IPython交互式环境进行示例。具体内容如下:
 决策树简介
 数据加载
 数据探索
 创建决策树模型
 训练模型并进行预测
 利用交叉验证、网格参数等进行模型调优
 最后生成一个可执行python脚本

11.1 PySpark 简介

在Spark的官网上这么介绍PySpark:“PySpark is the Python API for Spark”,也就是说PySpark其实是Spark为Python提供的编程接口。此外,Spark还提供了关于Scala、Java和R的编程接口,关于Spark为R提供的编程接口(Spark R)将在第12章进行介绍。

11.2 决策树简介

决策树在机器学习中是很常见且经常使用的模型,它是一个强大的非概率模型,可以用来表达复杂的非线性模式和特征相互关系。

图11-2决策树结构

关于决策树的原理,这里不再赘述。本章着重讨的是,决策树的分类模型在PySpark中的应用。

11.3数据加载

11.3.1 原数据集初探

这里的数据选择为某比赛的数据集,用来预测推荐的一些页面是短暂(昙花一现)还是长久(长时流行)。原数据集为train.tsv,存放路径在 /home/hadoop/data/train.tsv。
先使用shell命令对数据进行试探性的查看,并做一些简单的数据处理。
1) 查看前2行数据

数据集中的第1行为标题(字段名)行,下面是一些的字段说明。
查看文件记录总数

结果显示共有:数据集一共有7396条数据
2) 由于textFile目前不好过滤标题行数据,为便于spark操作数据,需要先删除标题。

3) 将数据文件上传到 hdfs

4) 查看是否成功

11.3.2 PySpark 的启动

以spark Standalone模式启动spark集群,保证内存分配充足。

[注]:使用pyspark --help 可以查看指令的详细帮助信息。

11.3.3 基本函数

这里将本章节中需要用到函数和方法做一个简单的说明,如表11-4所示。
表11-4 本章使用的一些函数或方法简介

11.4数据探索

1) 通过sc对象的textFile方法,载入本地数据文件,创建RDD

2) 查看第1行数据

3) 查看数据文件的总行数

4) 按键进行统计

原数据文件总的行数为7396,由于我们在数据加载中将数据集的第一行数据已经去除掉,所以这里结果为7395。

11.5数据预处理

1) 由于后续的算法我们不需要时间戳以及网页的内容,所以这里先将其过滤掉。

2) 查看records 数据结构

3) 查看每一行的列数

导入Vectors 矢量方法

导入决策树分类器

4) 将RDD中的所有元素以列表的形式返回

5) 查看data数据一行有多少列

6) 定义一个列表data1,存放清理过的数据,格式为[(label_1, features_1), (label_2, features_2),…]

对数据进行清理工作中的1,2,3步

11.6创建决策树模型

1) 将data1 转换为DataFrame对象,label表示标签列,features 表示特征值列

2) 由于后面会经常使用,所以将df载入内存

3) 建立特征索引

4) 将数据切分成80%训练集和20%测试集

5) 指定决策树模型的深度、标签列,特征值列,使用信息熵(entropy)作为评估方法,并训练数据。

6) 构建流水线工作流

下面我们用一组已知数据和一组新数据重新预测下结果:

11.7训练模型进行预测

1) 使用第一行数据进行预测结果,看看是否相符合,这里先来看一下原数据集第一行数据

2) 使用数据集中第一行的特征值数据进行预测

3) 将第一行的特征值数据修改掉2个(这里换掉第一个和第二个值),进行该特征值下的预测.

4) 进行新数据的预测

5) 下面我们用测试数据做决策树准确度测试

11.8模型优化
在上一个小节中,我们发现使用决策树的正确率不算高,只有63.3850%。在这一小节,我们探究一下改进预测准确率的方法。

11.8.1特征值的优化

1) 先将之前用到的一些代码加载进来。

2) 由于这里对网页类型的标识有很多,需要单独挑选出来进行处理。

将网页的唯一类型删选出来,并进行排序。

3) 查看网页类型的个数。

4) 紧接着,我们定义一个函数,用于返回当前网页类型的列表。

5) 通过这样的处理,我们将网页类型这一个特征值转化14个特征值,整体的特征值其实就增加了14个。接下来,我们在处理的时候将这个些特征值加入进去。

6) 创建DataFrame对象。

7) 建立特征索引。

8) 将数据切分成80%训练集和20%测试集。

9) 创建决策树模型。

10) 构建流水线工作流。

可以看到,准确率增大到了63.3850%,而未做优化前的准确率是65.2057%。增长了1.88%。效果还是比较显著的。

11.8.2交叉验证和网格参数

创建交叉验证和网格参数

通过交叉验证来训练模型

测试模型

准确率统计

11.9脚本方式运行

11.9.1 在脚本中添加配置信息

创建一个decisionTree.py文件,添加如下代码来配置启动pyspark。将上述在pyspark的IPython中的代码添加到该文件中来。
本文的示例程序存为 /home/hadoop/projects/spark/pyspark/decisionTree.py。

11.9.2运行脚本程序

spark 2.0 之前使用pyspark decisionTree.py 来执行文件,spark 2.0之后统一用spark-submit decisionTree.py 执行文件。读者可以使用spark-submit --help来查看相关命令的帮助信息。

11.10小结

本章我们了Spark中的 PySpark使用方法,对于PySpark做了简单的介绍。讨论了分类模型中最常见的决策树模型在PySpark 的应用,用实例讲解了如何对数据进行清理、转换,分析了分类模型的准确性有待提高的的原因,通过可视化对决策树的不同深度下的准确度进行讨论。

第1章:Keras基础

1.1Keras简介

Tensorflow、theano是神经网络、机器学习的基础框架,但使用它们大家神经网络,尤其深度学习网络,像tensorflow或theano属于符号编程,需要涉及如何定义变量、图形、各层、session、初始化、各种算法等等,有时显得比较繁琐,尤其对新手而言,更是如此,是否有更简单的方法呢?keras就是一个很好工具!Keras是一个高层神经网络API,Keras由纯Python编写而成并基Tensorflow、Theano以及CNTK后端。Keras 为支持快速实验而生,能够把你的想法迅速转换为结果。
其主要特点:

  • 简便的原型设计
  • 支持CNN和RNN,或二者的结合
  • 在CPU和GPU间无缝切换

keras资料:
keras官网:
https://keras.io/
keras中文网
https://keras-cn.readthedocs.io/en/latest/

1.2keras安装

1)安装Python3.6
建议用anaconda安装,先下载最新版anaconda 支持linux或windows
2)安装numpy、scipy

3)安装theano

4) 安装tensorflow

(gpu 需要有GPU卡,并安装GPU驱动及cuda等)
5)安装keras

6)测试
【说明】变更keras后台支持的几种方法

(1)修改~/.keras/keras.json

(2)在客户端直接修改
改为theano为支持后台

改为tensorflow为支持后台(缺省)

在客户端修改,影响范围是当前脚本或session。

1.3 keras常用概念

François Chollet作为人工智能时代的先行者,为无数的开发者提供了开源深度学习框架Keras,目前就职于Google公司,主推tf.keras。
在开始学习Keras之前,我们希望传递一些关于Keras,关于深度学习的基本概念和技术,我们建议新手在使用Keras之前浏览一下本页面提到的内容,这将减少你学习中的困惑。

  • 符号计算

Keras的底层库使用Theano或TensorFlow,这两个库也称为Keras的后端。无论是Theano还是TensorFlow,都是一个“符号式”的库。
因此,这也使得Keras的编程与传统的Python代码有所差别。笼统的说,符号主义的计算首先定义各种变量,然后建立一个“计算图”,计算图规定了各个变量之间的计算关系。建立好的计算图需要编译以确定其内部细节,然而,此时的计算图还是一个“空壳子”,里面没有任何实际的数据,只有当你把需要运算的输入放进去后,才能在整个模型中形成数据流,从而形成输出值。
就像用管道搭建供水系统,当你在拼水管的时候,里面是没有水的。只有所有的管子都接完了,才能送水。
符号计算也叫数据流图,如下图是一个经典的数据流计算可视化图形。
saddle_point_evaluation_optimizers

  • 张量

张量,或tensor,可以看作是向量、矩阵的自然推广,用来表示广泛的数据类型。张量的阶数也叫维度。
0阶张量,即标量,是一个数。
1阶张量,即向量,一组有序排列的数
2阶张量,即矩阵,一组向量有序的排列起来
3阶张量,即立方体,一组矩阵上下排列起来
4阶张量......
依次类推
重点:关于维度的理解
假如有一个10长度的列表,那么我们横向看有10个数字,也可以叫做10维度,纵向看只能看到1个数字,那么就叫1维度。注意这个区别有助于理解Keras或者神经网络中计算时出现的维度问题
张量的阶数有时候也称为维度,或者轴,轴这个词翻译自英文axis。譬如一个矩阵[[1,2],[3,4]],是一个2阶张量,有两个维度或轴,沿着第0个轴(为了与python的计数方式一致,本文档维度和轴从0算起)你看到的是[1,2],[3,4]两个向量,沿着第1个轴你看到的是[1,3],[2,4]两个向量。
 数据格式(data_format)
目前主要有两种方式来表示张量:
a) th模式或channels_first模式,Theano和caffe使用此模式。
b)tf模式或channels_last模式,TensorFlow使用此模式。
模式的修改,可以通修改配置文件~/.keras/keras.json中的image_data_format。

下面举例说明两种模式的区别:
对于100张RGB3通道的16×32(高为16宽为32)彩色图,
th表示方式:(100,3,16,32)
tf表示方式:(100,16,32,3)
唯一的区别就是表示通道个数3的位置不一样。

  • 模型

Keras有两种类型的模型,序贯(或序列)模型(Sequential)和函数式模型(Model),函数式模型应用更为广泛,序贯模型是函数式模型的一种特殊情况。
a)序贯模型(Sequential):单输入单输出,一条路通到底,层与层之间只有相邻关系,没有跨层连接。这种模型编译速度快,操作也比较简单
b)函数式模型(Model):多输入多输出,层与层之间任意连接。这种模型编译速度慢。

  • batch

这个概念与Keras无关,老实讲不应该出现在这里的,但是因为它频繁出现,而且不了解这个技术的话看函数说明会很头痛,这里还是简单说一下。
深度学习的优化算法,说白了就是梯度下降。每次的参数更新有两种方式。
第一种,遍历全部数据集算一次损失函数,然后算函数对各个参数的梯度,更新梯度。这种方法每更新一次参数都要把数据集里的所有样本都看一遍,计算量开销大,计算速度慢,不支持在线学习,这称为Batch gradient descent,批梯度下降。
另一种,每看一个数据就算一下损失函数,然后求梯度更新参数,这个称为随机梯度下降,stochastic gradient descent。这个方法速度比较快,但是收敛性能不太好,可能在最优点附近晃来晃去,hit不到最优点。两次参数的更新也有可能互相抵消掉,造成目标函数震荡的比较剧烈。
为了克服两种方法的缺点,现在一般采用的是一种折中手段,mini-batch gradient decent,小批的梯度下降,这种方法把数据分为若干个批,按批来更新参数,这样,一个批中的一组数据共同决定了本次梯度的方向,下降起来就不容易跑偏,减少了随机性。另一方面因为批的样本数与整个数据集相比小了很多,计算量也不是很大。
基本上现在的梯度下降都是基于mini-batch的,所以Keras的模块中经常会出现batch_size,就是指这个。

  • epochs

epochs指的就是训练过程中数据将被“轮”多少次。

1.4 keras与Tensorflow

1.5 keras的主要模块

【说明】
这里选择了一些常用模块,更多或更详细的说明请参考keras中文网站:
https://keras-cn.readthedocs.io/en/latest/
我们先从总体上了解一下Keras的主要模块及常用层,可参考下图,然后我们对各模块和常用层展开详细说明。

该图取自:http://blog.csdn.net/zdy0_2004/article/details/74736656

1.5.1优化器(optimizers)

优化器是调整每个节点权重的方法,看一个代码示例:

可以看到优化器在模型编译前定义,作为编译时的两个参数之一。
代码中的sgd是随机梯度下降算法
lr表示学习速率
momentum表示动量项
decay是学习速率的衰减系数(每个epoch衰减一次)
Nesterov的值是False或者True,表示使不使用Nesterov momentum
除了sgd,还可以选择的优化器有RMSprop(适合递归神经网络)、Adagrad、Adadelta、Adam、Adamax、Nadam等。

1.5.2目标函数(objectives)

目标函数又称损失函数(loss),目的是计算神经网络的输出与样本标记的差的一种方法,代码示例:

mean_squared_error就是损失函数的名称。
可以选择的损失函数有:
mean_squared_error,mean_absolute_error,squared_hinge,hinge,binary_crossentropy,categorical_crossentropy
其中binary_crossentropy 和 categorical_crossentropy也就是交叉熵为logloss,一般用于分类模型。

1.5.3激活函数(activations)

每一个神经网络层都需要一个激活函数,代码示例:

可以选择的激活函数有:
linear、sigmoid、hard_sigmoid、tanh、softplus、relu、 softplus,softmax、softsign
还有一些高级激活函数,比如如PReLU,LeakyReLU等。

1.5.4 参数初始化(Initializations)

这个模块的作用是在添加layer时调用init进行这一层的权重初始化,有两种初始化方法

1.5.4.1 通过制定初始化方法的名称

示例代码:

可以选择的初始化方法有:
uniform、lecun_uniform、normal、orthogonal、zero、glorot_normal、he_normal等。

1.5.4.2 通过调用对象

该对象必须包含两个参数:shape(待初始化的变量的shape)和name(该变量的名字),该可调用对象必须返回一个(Keras)变量,例如K.variable()返回的就是这种变量,示例代码:

或者

所以说可以通过库中的方法设定每一层的初始化权重,
也可以自己初始化权重,自己设定的话可以精确到每个节点的权重。

1.5.5 常用层(layer)

keras的层主要包括:
常用层(Core)、卷积层(Convolutional)、池化层(Pooling)、局部连接层、递归层(Recurrent)、嵌入层( Embedding)、高级激活层、规范层、噪声层、包装层,当然也可以编写自己的层

1.5.5.1 Dense层(全连接层)

keras.layers.core.Dense(units, activation=None, use_bias=True, kernel_initializer='glorot_uniform', bias_initializer='zeros', kernel_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, bias_constraint=None)
Dense就是常用的全连接层,所实现的运算是output = activation(dot(input, kernel)+bias)。其中activation是逐元素计算的激活函数,kernel是本层的权值矩阵,bias为偏置向量,只有当use_bias=True才会添加。如果本层的输入数据的维度大于2,则会先被压为与kernel相匹配的大小。
参数:
units:大于0的整数,代表该层的输出维度。
activation:激活函数,为预定义的激活函数名(参考激活函数),或逐元素(element-wise)的Theano函数。如果不指定该参数,将不会使用任何激活函数(即使用线性激活函数:a(x)=x)
use_bias: 布尔值,是否使用偏置项
kernel_initializer:权值初始化方法,为预定义初始化方法名的字符串,或用于初始化权重的初始化器。参考initializers
bias_initializer:偏置向量初始化方法,为预定义初始化方法名的字符串,或用于初始化偏置向量的初始化器。参考initializers
kernel_regularizer:施加在权重上的正则项,为Regularizer对象
bias_regularizer:施加在偏置向量上的正则项,为Regularizer对象
activity_regularizer:施加在输出上的正则项,为Regularizer对象
kernel_constraints:施加在权重上的约束项,为Constraints对象
bias_constraints:施加在偏置上的约束项,为Constraints对象
输入
形如(batch_size, ..., input_dim)的nD张量,最常见的情况为(batch_size, input_dim)的2D张量。
输出
形如(batch_size, ..., units)的nD张量,最常见的情况为(batch_size, units)的2D张量。
示例

1.5.5.2 Flatten层

Flatten层用来将输入“压平”,即把多维的输入一维化,常用在从卷积层到全连接层的过渡。Flatten不影响batch的大小。
keras.layers.core.Flatten()
示例

1.5.5.3 dropout层

为输入数据施加Dropout。Dropout将在训练过程中每次更新参数时随机断开一定百分比(p)的输入神经元连接,Dropout层用于防止过拟合。
keras.layers.core.Dropout(p)

1.5.5.4 卷积层(Convolutional)

1.5.5.4.1 Conv1D层

keras.layers.convolutional.Conv1D(filters, kernel_size, strides=1, padding='valid', dilation_rate=1, activation=None, use_bias=True, kernel_initializer='glorot_uniform', bias_initializer='zeros', kernel_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, bias_constraint=None)
一维卷积层(即时域卷积),用以在一维输入信号上进行邻域滤波。当使用该层作为首层时,需要提供关键字参数input_shape。例如(10,128)代表一个长为10的序列,序列中每个信号为128向量。而(None, 128)代表变长的128维向量序列。
该层生成将输入信号与卷积核按照单一的空域(或时域)方向进行卷积。如果use_bias=True,则还会加上一个偏置项,若activation不为None,则输出为经过激活函数的输出。
参数
filters:卷积核的数目(即输出的维度)
kernel_size:整数或由单个整数构成的list/tuple,卷积核的空域或时域窗长度
strides:整数或由单个整数构成的list/tuple,为卷积的步长。任何不为1的strides均与任何不为1的dilation_rate均不兼容
padding:补0策略,为“valid”, “same” 或“causal”,“causal”将产生因果(膨胀的)卷积,即output[t]不依赖于input[t+1:]。当对不能违反时间顺序的时序信号建模时有用。参考WaveNet: A Generative Model for Raw Audio, section 2.1.。“valid”代表只进行有效的卷积,即对边界数据不处理。“same”代表保留边界处的卷积结果,通常会导致输出shape与输入shape相同。
activation:激活函数,为预定义的激活函数名(参考激活函数),或逐元素(element-wise)的Theano函数。如果不指定该参数,将不会使用任何激活函数(即使用线性激活函数:a(x)=x)
dilation_rate:整数或由单个整数构成的list/tuple,指定dilated convolution中的膨胀比例。任何不为1的dilation_rate均与任何不为1的strides均不兼容。
use_bias:布尔值,是否使用偏置项
kernel_initializer:权值初始化方法,为预定义初始化方法名的字符串,或用于初始化权重的初始化器。参考initializers
bias_initializer:权值初始化方法,为预定义初始化方法名的字符串,或用于初始化权重的初始化器。参考initializers
kernel_regularizer:施加在权重上的正则项,为Regularizer对象
bias_regularizer:施加在偏置向量上的正则项,为Regularizer对象
activity_regularizer:施加在输出上的正则项,为Regularizer对象

kernel_constraints:施加在权重上的约束项,为Constraints对象
bias_constraints:施加在偏置上的约束项,为Constraints对象
输入shape
形如(samples,steps,input_dim)的3D张量。
输出shape
形如(samples,new_steps,nb_filter)的3D张量,因为有向量填充的原因,steps的值会改变。

1.5.5.4.2 Conv2D层

keras.layers.convolutional.Conv2D(filters, kernel_size, strides=(1, 1), padding='valid', data_format=None, dilation_rate=(1, 1), activation=None, use_bias=True, kernel_initializer='glorot_uniform', bias_initializer='zeros', kernel_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, bias_constraint=None)
二维卷积层,即对图像的空域卷积。该层对二维输入进行滑动窗卷积,当使用该层作为第一层时,应提供input_shape参数。例如input_shape = (128,128,3)代表128*128的彩色RGB图像(data_format='channels_last')

1.5.5.4.3 Conv3D层

keras.layers.convolutional.Conv3D(filters, kernel_size, strides=(1, 1, 1), padding='valid', data_format=None, dilation_rate=(1, 1, 1), activation=None, use_bias=True, kernel_initializer='glorot_uniform', bias_initializer='zeros', kernel_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, bias_constraint=None)
三维卷积对三维的输入进行滑动窗卷积,当使用该层作为第一层时,应提供input_shape参数。例如input_shape = (3,10,128,128)代表对10帧128*128的彩色RGB图像进行卷积。数据的通道位置仍然有data_format参数指定。

1.5.5.5池化层

1.5.5.5.1 MaxPooling1D层

keras.layers.pooling.MaxPooling1D(pool_size=2, strides=None, padding='valid')
对时域1D信号进行最大值池化
参数
pool_size:整数,池化窗口大小
strides:整数或None,下采样因子,例如设2将会使得输出shape为输入的一半,若为None则默认值为pool_size。
padding:‘valid’或者‘same’
输入shape
形如(samples,steps,features)的3D张量
输出shape
形如(samples,downsampled_steps,features)的3D张量

1.5.5.5.2 MaxPooling2D层

keras.layers.pooling.MaxPooling2D(pool_size=(2, 2), strides=None, padding='valid', data_format=None)
为空域信号施加最大值池化

1.5.5.5.3 AveragePooling1D层

keras.layers.pooling.AveragePooling1D(pool_size=2, strides=None, padding='valid')
对时域1D信号进行平均值池化

1.5.5.5.4 AveragePooling2D层

keras.layers.pooling.AveragePooling2D(pool_size=(2, 2), strides=None, padding='valid', data_format=None)
为空域信号施加平均值池化

1.5.5.6 循环层(Recurrent)

循环层包含三种模型:LSTM、GRU和SimpleRNN。
所有的循环层(LSTM,GRU,SimpleRNN)都继承本层,因此下面的参数可以在任何循环层中使用。

1.5.5.6.1抽象层,不能直接使用

keras.layers.recurrent.Recurrent(return_sequences=False, go_backwards=False, stateful=False, unroll=False, implementation=0)
weights:numpy array的list,用以初始化权重。该list形如[(input_dim, output_dim),(output_dim, output_dim),(output_dim,)]
 return_sequences:布尔值,默认False,控制返回类型。若为True则返回整个序列,否则仅返回输出序列的最后一个输出
 go_backwards:布尔值,默认为False,若为True,则逆向处理输入序列并返回逆序后的序列
 stateful:布尔值,默认为False,若为True,则一个batch中下标为i的样本的最终状态将会用作下一个batch同样下标的样本的初始状态。
 unroll:布尔值,默认为False,若为True,则循环层将被展开,否则就使用符号化的循环。当使用TensorFlow为后端时,循环网络本来就是展开的,因此该层不做任何事情。层展开会占用更多的内存,但会加速RNN的运算。层展开只适用于短序列。
 implementation:0,1或2, 若为0,则RNN将以更少但是更大的矩阵乘法实现,因此在CPU上运行更快,但消耗更多的内存。如果设为1,则RNN将以更多但更小的矩阵乘法实现,因此在CPU上运行更慢,在GPU上运行更快,并且消耗更少的内存。如果设为2(仅LSTM和GRU可以设为2),则RNN将把输入门、遗忘门和输出门合并为单个矩阵,以获得更加在GPU上更加高效的实现。注意,RNN dropout必须在所有门上共享,并导致正则效果性能微弱降低。
 input_dim:输入维度,当使用该层为模型首层时,应指定该值(或等价的指定input_shape)
 input_length:当输入序列的长度固定时,该参数为输入序列的长度。当需要在该层后连接Flatten层,然后又要连接Dense层时,需要指定该参数,否则全连接的输出无法计算出来。注意,如果循环层不是网络的第一层,你需要在网络的第一层中指定序列的长度(通过input_shape指定)。

1.5.5.6.2全连接RNN网络

keras.layers.SimpleRNN(units, activation='tanh', use_bias=True, kernel_initializer='glorot_uniform', recurrent_initializer='orthogonal', bias_initializer='zeros', kernel_regularizer=None, recurrent_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, recurrent_constraint=None, bias_constraint=None, dropout=0.0, recurrent_dropout=0.0, return_sequences=False, return_state=False, go_backwards=False, stateful=False, unroll=False)
全连接RNN网络,RNN的输出会被回馈到输入
参数说明
• units:输出维度
• activation:激活函数,为预定义的激活函数名(参考激活函数)
• use_bias: 布尔值,是否使用偏置项
• kernel_initializer:权值初始化方法,为预定义初始化方法名的字符串,或用于初始化权重的初始化器。参考initializers
• recurrent_initializer:循环核的初始化方法,为预定义初始化方法名的字符串,或用于初始化权重的初始化器。参考initializers
• bias_initializer:权值初始化方法,为预定义初始化方法名的字符串,或用于初始化权重的初始化器。参考initializers
• kernel_regularizer:施加在权重上的正则项,为Regularizer对象
• bias_regularizer:施加在偏置向量上的正则项,为Regularizer对象
• recurrent_regularizer:施加在循环核上的正则项,为Regularizer对象
• activity_regularizer:施加在输出上的正则项,为Regularizer对象
• kernel_constraints:施加在权重上的约束项,为Constraints对象
• recurrent_constraints:施加在循环核上的约束项,为Constraints对象
• bias_constraints:施加在偏置上的约束项,为Constraints对象
• dropout:0~1之间的浮点数,控制输入线性变换的神经元断开比例
• recurrent_dropout:0~1之间的浮点数,控制循环状态的线性变换的神经元断开比例
• 其他参数参考Recurrent的说明

输入shape
形如(samples,timesteps,input_dim)的3D张量
输出shape
如果return_sequences=True:返回形如(samples,timesteps,output_dim)的3D张量
否则,返回形如(samples,output_dim)的2D张量
示例:

1.5.5.6.3 LSTM层

keras.layers.recurrent.LSTM(units, activation='tanh', recurrent_activation='hard_sigmoid', use_bias=True, kernel_initializer='glorot_uniform', recurrent_initializer='orthogonal', bias_initializer='zeros', unit_forget_bias=True, kernel_regularizer=None, recurrent_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, recurrent_constraint=None, bias_constraint=None, dropout=0.0, recurrent_dropout=0.0)
Keras长短期记忆模型
forget_bias_init:遗忘门偏置的初始化函数,建议初始化为全1元素。
inner_activation:内部单元激活函数

1.5.5.6.4 GRU

keras.layers.recurrent.GRU(units, activation='tanh', recurrent_activation='hard_sigmoid', use_bias=True, kernel_initializer='glorot_uniform', recurrent_initializer='orthogonal', bias_initializer='zeros', kernel_regularizer=None, recurrent_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, recurrent_constraint=None, bias_constraint=None, dropout=0.0, recurrent_dropout=0.0)
门限循环单元

1.5.5.6.5Embedding层

keras.layers.embeddings.Embedding(input_dim, output_dim, init='uniform', input_length=None, W_regularizer=None, activity_regularizer=None, W_constraint=None, mask_zero=False, weights=None, dropout=0.0)
只能作为模型第一层
mask_zero:布尔值,确定是否将输入中的‘0’看作是应该被忽略的‘填充’(padding)值,该参数在使用递归层处理变长输入时有用。设置为True的话,模型中后续的层必须都支持masking,否则会抛出异常

1.5.6 model层

model层是最主要的模块,model层可以将上面定义了各种基本组件组合起来。
model的方法:
model.summary() : 打印出模型概况
model.get_config() :返回包含模型配置信息的Python字典
model.get_weights():返回模型权重张量的列表,类型为numpy array
model.set_weights():从numpy array里将权重载入给模型
model.to_json:返回代表模型的JSON字符串,仅包含网络结构,不包含权值。可以从JSON字符串中重构原模型:

model.save_weights(filepath):将模型权重保存到指定路径,文件类型是HDF5(后缀是.h5)。
model.load_weights(filepath, by_name=False):从HDF5文件中加载权重到当前模型中, 默认情况下模型的结构将保持不变。如果想将权重载入不同的模型(有些层相同)中,则设置by_name=True,只有名字匹配的层才会载入权重。
keras有两种model,分别是Sequential模型和泛型模型。

1.5.6.1 Sequential模型

Sequential是多个网络层的线性堆叠
可以通过向Sequential模型传递一个layer的list来构造该模型:

也可以通过.add()方法一个个的将layer加入模型中:

还可以通过merge将两个Sequential模型通过某种方式合并
Merge层提供了一系列用于融合两个层或两个张量的层对象和方法。以大写首字母开头的是Layer类,以小写字母开头的是张量的函数。小写字母开头的张量函数在内部实际上是调用了大写字母开头的层。
keras.engine.topology.Merge(layers=None, mode='sum', concat_axis=-1, dot_axes=-1, output_shape=None, node_indices=None, tensor_indices=None, name=None)
layers:该参数为Keras张量的列表,或Keras层对象的列表。该列表的元素数目必须大于1。
mode:合并模式,如果为字符串,则为下列值之一{“sum”,“mul”,“concat”,“ave”,“cos”,“dot”}
其中sum和mul是对待合并层输出做一个简单的求和、乘积运算,因此要求待合并层输出shape要一致。concat是将待合并层输出沿着最后一个维度进行拼接,因此要求待合并层输出只有最后一个维度不同。
Merge是一个层对象,在多个sequential组成的网络模型中,如果
x:输入数据。如果模型只有一个输入,那么x的类型是numpy array,如果模型有多个输入,那么x的类型应当为list,list的元素是对应于各个输入的numpy array
y:标签,numpy array
否则运行时很可能会提示意思就是你输入的维度与实际不符
 Add
keras.layers.Add()
添加输入列表的图层。
该层接收一个相同shape列表张量,并返回它们的和,shape不变。

 Concatenate
keras.layers.Concatenate(axis=-1)
该层接收一个列表的同shape张量,并返回它们的按照给定轴相接构成的向量。

1.5.6.2 函数式(Functional)模型

在Keras 2里我们将这个词改译为“函数式”,对函数式编程有所了解的同学应能够快速get到该类模型想要表达的含义。函数式模型称作Functional,但它的类名是Model,因此我们有时候也用Model来代表函数式模型。
Keras函数式模型接口是用户定义多输出模型、非循环有向模型或具有共享层的模型等复杂模型的途径。一句话,只要你的模型不是类似VGG一样一条路走到黑的模型,或者你的模型需要多于一个的输出,那么你总应该选择函数式模型。函数式模型是最广泛的一类模型,序贯模型(Sequential)只是它的一种特殊情况。
这部分的文档假设你已经对Sequential模型已经比较熟悉
让我们从简单一点的模型开始
第一个模型:全连接网络
Sequential当然是实现全连接网络的最好方式,但我们从简单的全连接网络开始,有助于我们学习这部分的内容。在开始前,有几个概念需要澄清:
层对象接受张量为参数,返回一个张量。
输入是张量,输出也是张量的一个框架就是一个模型,通过Model定义。
这样的模型可以被像Keras的Sequential一样被训练

所有的模型都是可调用的,就像层一样
利用函数式模型的接口,我们可以很容易的重用已经训练好的模型:你可以把模型当作一个层一样,通过提供一个tensor来调用它。注意当你调用一个模型时,你不仅仅重用了它的结构,也重用了它的权重。

使用函数式模型的一个典型场景是搭建多输入、多输出的模型,如下图:

第2章 keras的使用流程

2.1 流程说明

第1步:构造数据:定义输入数据
第2步:构造模型:确定各个变量之间的计算关系
第3步:编译模型:编译已确定其内部细节
第4步:训练模型:导入数据,训练模型
第5步:测试模型
第6步:保存模型
把这些步骤进一步图形化为:

2.2 实例-详细说明使用流程

第1步:构造数据
我们需要根据模型fit(训练)时需要的数据格式来构造数据的shape,这里我们用numpy构造两个矩阵:一个是数据矩阵,一个是标签矩阵。

通过numpy的random生成随机矩阵,数据矩阵是1000行784列的矩阵,标签矩阵是1000行1列的句子,所以数据矩阵的一行就是一个样本,这个样本是784维的。
第2步 构造模型
我们来构造一个神经网络模型,keras构造深度学习模型可以采用序列模型(基于Sequential类)或函数模型(又称为通用模型)(基于Model类)。两种间差异是拓扑结构不一样。这里我们采用序列模型。

在这一步中可以add多个层,也可以merge合并两个模型。
第3步:编译模型
我们编译上一步构造好的模型,并指定一些模型的参数,optimizer(优化器),loss(目标函数或损失函数),metrics(评估模型的指标)等。编译模型时损失函数和优化器这两项是必须的。

第4步:训练模型
传入要训练的数据和标签,并指定训练的一些参数,然后进行模型训练。

Epoch 1/10
- 1s - loss: 0.7063 - acc: 0.5010
Epoch 2/10
- 0s - loss: 0.6955 - acc: 0.5110
.................................
Epoch 10/10
- 0s - loss: 0.5973 - acc: 0.6980

epochs:整数,训练的轮数。
verbose:训练时显示实时信息,0表示不显示数据,1表示显示进度条,2表示用只显示一个数据。
batch_size:整数,指定进行梯度下降时每个batch包含的样本数。训练时一个batch的样本会被计算一次梯度下降,使目标函数优化一步。
第5步:测试模型
用测试数据测试已经训练好的模型,并可以获得测试结果,从而对模型进行评估

200/200 [==============================] - 0s 146us/step
本函数返回一个测试误差的标量值(如果模型没有其他评价指标),或一个标量的list(如果模型还有其他的评价指标)
第6步:保存模型

【项目延伸】
上面是采用全连接的神经网络,包括输入层、一个隐含层及一个输出层。如果我们卷积神经网络是否可以?例如:一个卷积层+池化层+展平+全连接+输出层。

第3章 keras实现单层神经网络

3.1利用keras实现单层神经

本章利用Keras架构实现一个传统机器学习算法---线性回归
根据输入数据及目标数据,模拟一个线性函数y=kx+b
这里使用一个神经元,神经元中使用Relu作为激活函数。如下图:

第1步:构造数据

把200份数据划分为训练数据、测试数据。

第2步 构造模型

第3步 编译模型

第4步 训练模型

第5步 测试模型

Testing ------------
40/40 [==============================] - 0s 996us/step
test cost: 0.00395184289664
Weights= [[ 0.48489931]]
biases= [ 1.95838749]

可视化结果:

第4章 keras实现多层神经网络

利用keras构造一个多层神经网络,用该神经网络识别手写数字,上次我们采用python来实现,这里我们采用keras来构造多层神经网络。
网络构造图形:

在整个网络设计中,输入数据的维度,优化方法、损失函数需要重点考虑。当然激活函数也很重要,特别是层数较多时。
这里为便于说明keras构建多层神经网络的方法,采用MNIST数据集,MNIST是一个手写数字0-9的数据集,它有60000个训练样本集和10000个测试样本集它是NIST数据库的一个子集。该数据集keras有现成的数据处理API(mnist.load_data())。
数据预处理:
(1)展平矩阵:
原数据为28*28图片,在利用全连接前,需要把矩阵拉平为一维数组,大小为784;
(2)规范训练数据
转换为都是0-255的像素,为提高模型的泛化能力,需要对数据规范化,即除以255,使数据范围都在[0,1]之间;
(3)规范标签数据
把标签数据转换为one-hot格式,向量维度为10,每行除一个1元素外,其它都是0,如把2标签转换为[0,0,1,0,0,0,0,0,0,0]
以下为详细计算步骤:
第1步: 构建数据

运行结果:
Using TensorFlow backend.
展平前
(60000, 28, 28) (60000,)
展平后
(60000, 784) (60000, 10)

第2步 构建网络

第3步 编译模型

运行结果:
【备注】
如果我们需要对优化方法进行某些定制化,也很方便:

第4步 训练模型

运行结果:
Training ------------
Epoch 1/2
- 7s - loss: 0.1584 - acc: 0.9539
Epoch 2/2
- 6s - loss: 0.1340 - acc: 0.9607
第5步 测试模型

运行结果:
test loss: 0.132445694927
test accuracy: 0.9614


本章数据集下载

第10章 构建Spark ML聚类模型

前面我们介绍了推荐、分类、回归等模型,这些模型属于监督学习,在训练模型时,都提供目标值或标签数据,根据目标值训练模型,然后根据模型对测试数据或新数据进行推荐、分类或预测。
但实际数据有很多是没有标签数据,或者预先标签很难,但我们又希望或需要从这些数据中提炼一些规则或特征等,如识别异常数据、对客户进行分类等,解决这类问题就属于无监督学习。
聚类是一种无监督学习,它与分类的不同,聚类所要求划分的类是未知的。
聚类算法的思想就是物以类聚的思想,相同性质的点在空间中表现的较为紧密和接近,主要用于数据探索与异常检测。
聚类分析是一种探索性的分析,在分类的过程中,人们不必事先给出一个分类的标准,它能够从样本数据出发,自动进行分类。聚类分析也有很多方法,使用不同方法往往会得到不同的结论。从实际应用的角度看,聚类分析是数据挖掘的主要任务之一。而且聚类能够作为一个独立的工具获得数据的分布状况,观察每一簇数据的特征,集中对特定的聚簇集合作进一步地分析。聚类分析还可以作为其他算法(如分类和推荐等算法)的预处理步骤。

10.1 K-means模型简介

作为经典的聚类算法,一般的机器学习框架里都有K-means,Spark自然也不例外。
不过spark中的K-means,除有一般K-means的特点外,还进行了如下的优化:

10.2 数据加载

这里我们以某批发经销商的客户对不同产品的年度消费支出(数据来源http://archive.ics.uci.edu/ml/datasets/Wholesale+customers)
读取HDFS中的数据。

10.3 探索特征的相关性

从以上分析,我们可以看出,rawdata数据集总记录数为440条,最大与最小值相差不大,已统计的特征来看,没有缺失值,数据类型为字符型,这点需要在预处理中转换为Double型。
利用pyspark我们可以画出这些特征间的相关性,这里使用pearson's r,相关系统在[-1,1]之间,如果r=1,表示特征完全正相关;r=0,表示不存在关系;r=-1,表示特征完全负相关。
实现代码:

10.4 数据预处理

通过数据探索,发现数据需要又字符转换为数值型,并缓存。

查看数据的统计信息:

Channel、Region为类别型,其余6个字段为连续型,为此,在训练模型前,需要对类别特征先转换为二元向量,然后,对各特征进行规范化。最后得到一个新的特征向量。
对类别特征转换为二元编码:

把新生成的两个特征及原来的6个特征组成一个特征向量

把源数据组合成特征向量features

10.5 组装

这里我们只使用了setK、setSeed两个参数,其余的使用缺省值。

10.6 模型优化

聚类模型中最重要的是参数k的选择,下面我们通过循环来获取哪个k值的性能最好。

以上数据可视化的图形如图10-2所示。

图10-2聚类模型中族K与评估指标的关系

从图10-2中不难看出,k<12时,性能(computeCost)提升比较明显,>12后,逐渐变缓。所以K越大不一定越好,恰当才是重要的。
当k=10时,聚类结果如下:
+----------+-----+
|prediction|count|
+----------+-----+
| 1| 65|
| 6| 18|
| 3| 86|
| 5| 27|
| 9| 3|
| 4| 2|
| 8| 2|
| 7| 46|
| 2| 27|
| 0| 164|
+----------+-----+
图10-3为k取10时(族0对应的channel和Region分别为1和3;族3对应的channel和Region分别为2和3),前两大族的销售均值比较图,从图中可以看出,团购冷藏食品均值大于或接近零售冷藏食品均值。说明团购对冷藏食品量比较大。

图10-3 聚类模型中K=10时0和3族平均销售额对比

10.7 小结

本章主要介绍了用Spark ML中的聚类算法,对某地多种销售数据进行聚类分析,在分析前对数据集主要特征进行了相关性分析,并对类别数据进行二元向量化,对连续性数据进行规范和标准化,然后把这些stages组装在流水线上,在模型训练中,我们尝试不同K的取值,以便获取最佳族群数。


本章数据集下载

第9章 构建Spark ML回归模型

回归模型属于监督式学习,每个个体都有一个与之相关联的实数标签,并且我们希望在给出用于表示这些实体的数值特征后,所预测出的标签值可以尽可能接近实际值。
回归算法是试图采用对误差的衡量来探索变量之间的关系的一类算法。回归算法是统计机器学习的利器。在机器学习领域,人们说起回归,有时候是指一类问题,有时候是指一类算法,这一点常常会使初学者有所困惑。常见的回归算法包括:普通最小二乘法(OLS)(Ordinary Least Square),它使用损失函数是平方损失函数(1/2 (w^T x-y)^2),简单的预测就是y=w^T x,标准的最小二乘回归不使用正则化,这就意味着数据中异常数据点非常敏感,因此,在实际应用中经常使用一定程度的正则化(目的避免过拟合、提供泛化能力)。
本章主要介绍Spark ML中的回归模型,以回归分析中常用决策树回归、线性回归为例,对共享单车租赁的情况进行预测,其中介绍了一些特征转换、特征选择、交叉验证等方法的具体使用,主要内容包括:
回归模型简介
把数据加载到HDFS,Spark读取HDFS中的数据
探索特征及其分布信息
预处理数据
把pipeline的多个Stages组装到流水线上
模型优化

9.1 回归模型简介

ML目前支持回归模型有:
Linear regression (线性回归)
Generalized linear regression(广义线性回归)
Decision tree regression (决策树的回归)
Random forest regression(随机森林回归 )
Gradient-boosted tree regression (梯度提高树回归)
Survival regression(生存回归)
Isotonic regression(保序回归)

9.2 数据加载

查看数据大致情况:

从数据集前3行的数据可以看出,第一行为标题,其他为租赁数据,共有17个字段和17380条记录。
把数据文件hour.csv复制到HDFS上。

以独立模式启动spark,然后读取数据。

导入需要使用的类。

读取数据,把第一行为列名:

查看前4行样本数据

9.3 探索特征分布

Spark读取数据后,我们就可以对数据进行探索和分析,首先查看前4行样本数据

查看rawdata的数据结构

目前这些数据的字段都是字符型,后续需要转换为数值型。
查看主要字段的统计信息

其中有很多字段是类型型,如果使用回归算法时,需要通过OneHotEncoder把数据转换为二元向量,对一些字段或特征进行规范化。
通过pyspark可以画出主要特征的重要程度:

图9-2 各特征的重要性

通过pyspark可以画出其中一些特征的分布情况:

图9-3特征间的关系图

9.4 数据预处理

9.4.1 特征选择

首先把字符型的特征转换为数值类型,并过滤instant、dteday、casual、registered等4个无关或冗余特征。cnt特征作为标志。

生成一个存放以上预测特征的特征向量

把源数据组合成特征向量features

9.4.2 特征转换

使用决策树回归算法前,我们对类别特征进行索引化或数值化。

因OneHotEncoder不是Estimator,这里我们对采用回归算法的数据另外进行处理,先建立一个流水线,把以上转换组装到这个流水线上。

把原来的4个及转换后的8个二元特征向量,拼接成一个feature向量。

9.5 组装

1)将data1数据分为训练和测试集(30%进行测试,种子设为12):

2)设置决策树回归模型参数

3)设置线性回归模型的参数

4)把决策树回归模型涉及的特征转换及模型训练组装在一个流水线上。

5)把线性回归模型涉及的特征转换、模型训练组装载一个流水上线。

6)训练模型

7)作出预测

8)评估模型

从以上使用不同模型情况看来,决策树性能稍好与线性回归,但这仅是粗糙的比较,下面使用模型选择中介绍的一些方法,对线性模型进行优化。

9.6 模型优化

从图9-3可知,temp特征与atemp特征线性相关,而且从图9-2可知,atemp的贡献度较小,所以我们将过滤该特征。

对label标签特征进行转换,使其更接近正态分布,这里我们SQLTransformer转换器,其具体使用可参考第4章。

这里我们利用训练验证划分法对线性回归模型进行优化,对参数进行网格化,将数据集划分为训练集、验证集和测试集。
1)导入需要用到的包。

2)建立模型,预测label1的值,设置线性回归参数。

3)设置流水线,为便于把特征组合、特征值优化、模型训练等任务组装到这条流水线上。

4)建立参数网格。

5)选择(prediction, label1),计算测试误差。

6)训练模型并自动选择最优参数。

7)查看模型全部参数

8)用最好的参数组合,做出预测。

看了对标签特征进行转换、利用网格参数及训练验证划分等优化方法,从102下降到3左右,效果比较明显。

9.7 小结

本章主要介绍Spark ML的线性回归模型、决策树回归模型,对共享单车的租赁信息进行预测,由于很多数据不规范,因此,对原数据进行了二元向量转换、对类别数据索引化,然后把这些转换组装到流水线上,在训练集上训练模型,在测试集上进行预测,最后,更加评估指标对模型进行优化。

第8章 构建Spark ML 分类模型

在上一章中,我们通过实例介绍了Spark中基于协同过滤的推荐模型,了解了推荐模型的原理以及场景、使用流水线组装任务,使用自定义函数优化模型等。这一章我们将就Spark中分类模型为例,进一步说明如何使用Spark ML中特征选取、特征转换、流水线、模型选择或优化等方法,简化、规范化、流程化整个机器学习过程。
分类、回归和聚类是机器学习中重要的几个分支,也是日常数据处理与分析中最常用的手段。这几类的算法有着较高的成熟度,原理也较容易理解,且有着不错的效果,深受数据分析师们的喜爱。本章以Spark ML分类模型为例,主要包括以下内容:
 简介用于分类的几种常用算法
 加载数据
 探索加载后的数据
 预处理数据
 把各种任务组装到流水线上
 模型调优

8.1分类模型简介

8.1.1线性模型

8.1.2 决策树模型

决策树模型是一个强大的非概率模型,可以用来表示复杂的非线性模式和特征的相互关系。

8.1.3 朴素贝叶斯模型

关于朴素贝叶斯详细的原理,在维基百科中有更为详细的数学公式解释:http://en.wikipedia.org/wiki/Naive_Bayes_classifier。

8.2数据加载

存放路径在 /home/hadoop/data/train.tsv。
数据集下载
先使用shell命令对数据进行试探性的查看,并做一些简单的数据处理。
1) 查看前2行数据

数据集中的第1行为标题(字段名)行,下面是一些的字段说明。
2) 查看文件记录总数

结果显示共有:数据集一共有7396条数据
3) 由于textFile目前不好过滤标题行数据,为便于spark操作数据,需要先删除标题。

4) 将数据文件上传到 hdfs

5) 查看是否上成功

6) 启动Spark Shell

7) 通过sc对象的textFile方法,由本地文件数据创建RDD

8.3数据探索

1) 查看数据前2行

由上面可以看到,得到的是只有一行字符串数组。通过常看源文件,我们可以发现字段间由制表符(\t)分割。由于后续的算法我们不需要时间戳以及网页的内容,所以这里先将其过滤掉。下面我们获取每个属性。
2) 根据以上分析,对数据进行处理,并生成新的RDD

3) 查看数据结构

4) 查看总的数据行数

5) 查看每一行数据的列数

6) 获取第一行的某个值

8.4数据预处理

1) 导入LabeledPoint

2) 导入Vectors矢量方法

3) 对数据进行1-4步的数据清洗工作

上述代码可通过复制粘贴到代码行中,使用 :paste ,粘贴过后按下 Ctrl+D 即可。
4) 考虑到使用朴素贝叶斯算法时,数据需不小于0,故需要做些处理。

5) 查看清理后数据集的前2行数据

6) 通过RDD创建DataFrame

7) 查看df和nbDF的数据

8) 查看df和nbDF的Schema的信息和数据总行数

9) 随机地将数据进行划分,80%用于训练集,20%用于测试集

10) 查看训练数据和测试数据的总行数

11) 由于后续使用网格参数和交叉验证的时候,需要多次使用到训练集和测试集,所以将这两者载入内存,可大大提高性能。

12) 导入逻辑回归分类器、决策树模型以及朴素贝叶斯模型

13) 创建贝叶斯模型,设置初始参数

14) 通过朴素贝叶斯训练模型,对测试数据进行预测

15) 朴素贝叶斯准确性统计

可以看到,朴素贝叶斯的准确率为56.6419%。

8.5组装

1) 导入特征索引类

2) 建立特征索引

3) 创建逻辑回归模型

4) 创建决策树模型

5) 导入网格参数和交叉验证

6) 导入流水线

7) 导入评估器

8) 配置2个流水线:一个是逻辑回归的流水线,包含2个stages( featureIndexer和lr);
一个是决策树回归的流水线,包含2个stages( featureIndexer 和 dt)。

8.6模型优化

1) 分别配置网格参数,使用ParamGridBuilder构造一个parameter grid

2) 分别实例化交叉验证模型

3) 通过交叉验证模型,获取最优参数集,并测试模型

4) 查看数据

5) 查看逻辑回归匹配模型的参数

6) 查看决策树匹配模型的参数

7) 统计逻辑回归的预测正确率

可以看到,我们通过交叉验证得出最优参数,从而获得最佳模型,将这个过程使用流水线连接起来,方便了我们的工作。关于模型的优化,其实我们还有很多工作要做,第11章也也出了一定的优化思路和方法。

8.7小结

本章就Spark ML中分类模型进行的详细介绍,包括逻辑回归、决策树、朴素贝叶斯模型的原理,同时介绍了分类模型的一些使用场景。通过流水线、网格参数以及交叉验证的方式,将整个机器学习过程规范化、标准化、流程化。


本章数据集下载

第7章 构建Spark ML推荐模型

前面我们介绍了机器学习的一般步骤、如何探索数据、如何预处理数据、如何利用Spark Ml中的一些算法或API,以及有效处理机器学习过程中的特征转换、特征选择、训练模型,并把这些过程流程化等。从本章开始,我们将通过实例,进一步阐述这些问题,并通过实例把相关内容有机结合起来。
本章主要介绍Spark机器学习中的协同过滤(Collaborative Filtering,CF)模型,协调过滤简单来,说是利用某个兴趣相投、拥有共同经验之群体的喜好来推荐感兴趣的资讯给使用者,个人透过合作的机制给予资讯相当程度的回应(如评分)并记录下来以达到过滤的目的,进而帮助别人筛选资讯,回应不一定局限于特别感兴趣的,特别不感兴趣资讯的纪录也相当重要。在日常生活中,人们实际上经常使用这种方法,如你哪天突然想看个电影,但你不知道具体看哪部,你会怎么做?大部分的人会问问周围的朋友,最近有什么好看的电影,而我们一般更倾向于从兴趣或观点相近的朋友那里得到推荐。这就是协同过滤的思想。换句话说,就是借鉴和你相关人群的观点来进行推荐。
本章介绍Spark的推荐模型,将按以下步骤进行:
 首先简介推荐模型
 加载数据到HDFS
 Spark读取数据
 对数据进行探索
 训练模型
 组装任务
 评估、优化模型

7.1推荐模型简介

协同过滤常被用于推荐系统。这类技术目标在于填充“用户-商品”联系矩阵中的缺失项。Spark.ml目前支持基于模型的协同过滤,其中用户和商品以少量的潜在因子来描述,用以预测缺失项。Spark.ml使用交替最小二乘(ALS)算法来学习这些潜在因子。

7.2数据加载

这里使用MovieLens 100k数据集,主要包括用户属性数据(u.user)、电影数据(u.item)、用户对电影的评级数据(u.data)及题材数据(u.genre)等。在把数据复制到HDFS之前,我们先大致了解一下相关数据:
用户数据(u.user)结构:

可以看出用户数据由user id、age、gender、occupation和zip code等5个字段,字段间隔符为竖线("|"),共有943行。
电影数据(u.item)结构:

可以看出用户数据由movie id、title、release date及其他属性,字段间隔符为竖线("|"),共有1682行。
用户对电影评级数据(u.data)结构:

可以看出用户数据由user id、movie id、rating(1-5)和timestamp等4个字段,字段间隔符为制表符("\t"),共有100000行。
电影题材数据(u.genre):

这个数据只有两个字段:题材及代码,以竖线分隔。共有20种电影题材。
把用户数据(u.user)复制到HDFS上,其他数据方法一样。

查看数据复制是否成功

把相关数据复制到HDFS后,我们就可以利用Pyspark对数据进行探索或简单分析,这里使用Pyspark主要考虑其可视化功能,如果不需要数据的可视化,使用Spark即可。
以spark Standalone模式启动spark集群

导入需要的包或库

7.3数据探索

数据加载到HDFS后,我们便可对数据进行探索和分析,对用户数据的探索,大家可参考2.4.3节的相关内容。用户对电影评级数据比较简单,这里我们简单查看一下导入数据抽样及统计信息。 抽样数据:

用户ID、电影ID、评级数据统计信息:

由此可知,该数据集共有100000条,评级最低为1.0,最高为5.0,平均3.5左右。

7.4训练模型

这里数据比较简单,无须做数据转换和清理等数据预处理工作。在训练模型前,我们需要把数据划分为几个部分,这里先随机划分成两部分,划分比例为80%作为训练集,20%作为测试集。后续我们在性能优化时将采用另一种划分方式,然后,比较使用不同划分方法对模型性能或泛化能力的影响。

7.5组装

1)创建流水线,把数据转换、模型训练等任务组装在一条流水线上。

2)训练模型

3)作出预测

4)查看预测值与原来的值

7.6评估模型

1)预测时会产生NaN,即NaN表示不推荐(预测时产生NaN是spark2.1 ALS中的一个bug,该bug在2.2中将修复)

2)删除含NaN的值的行,NaN有一定合理性,不推荐,但为评估指标,可以先过滤这些数。

3)运行结果为:rmse: Double = 1.016902715345917

7.7模型优化

//最佳模型相关参数
The best model was trained with rank = 20 and lambda = 0.1, and numIter = 10, and its RMSE on the test set is 0.9383240505365207.

7.8小结

本章介绍了推荐模型的一般方法,Spark推荐模型的原理和算法等,然后通过一个实例具体说明实施Spark推荐模型的一般步骤、使用自定义函数优化模型等内容。下一章将以Spark ML的分类模型为例,进一步说明如何使用Spark ML提供的特征选取、特征转换、流水线、交叉验证等函数或方法。


本章数据集下载

第6章Spark MLlib基础

传统的机器学习算法,由于技术和单机存储的限制,只能在少量数据上使用。一旦数据量过大,往往需要采用数据抽样的方法。但这种抽样很难保证不走样。近些年随着 HDFS 等分布式文件系统出现,存储海量数据已经成为可能。在全量数据上进行机器学习变得可能或必要,但由于MapReduce计算框架,虽然实现分布式计算,但中间结果需要存在到磁盘,这对这计算过程中需要多次迭代的机器学习,因为通常情况下机器学习算法参数学习的过程都是迭代计算的,不很理想。
Spark的出现,正好弥补了MapReduce的不足,它立足于内存计算,所以特别适合机器学习的迭代式计算。同时Spark提供了一个基于海量数据的分布式运算的机器学习库,同时提供了很多特征选取、特征转换等内嵌函数,大大降低了大家学习和使用Spark的门槛,对很多开发者只需对 Spark 有一定基础、了解机器学习算法的基本原理、以及相关参数的含义和作用,一般都可以通过都可以比较顺利地使用Spark进行基于大数据的机器学习。
Spark在机器学习方面有很多优势,本章主要Spark与机器学习相关的内容。
 Spark MLlib简介
 Spark MLlib架构
 常用的几种数据类型
 基础统计
 RDD、DataFrame及Dataset间的异同
 Spark MLlib常用算法

6.1Spark MLlib简介

MLlib是MLBase一部分,其中MLBase分为四部分:MLlib、MLI、ML Optimizer和MLRuntime。它们的结构如下图:

图6-2 MLBase四部分关系

6.2Spark MLlib架构

6.3数据类型

Spark MLlib的数据类型主要分为四种,下面将分别介绍。
1. 本地向量(Local vector)
其创建方式主要有以下几种:(以下使用Scala语言)

2. 标记点(Labeled point)
标记点是由一个本地向量(密集或稀疏)和一个标签(整数或浮点)组成,这个值的具体内容可以由用户指定。

从文件中直接获取标记点:

3. 本地矩阵(Local matrix)
由行索引、列索引、类型值组成,存放在单机中。

4. 分布式矩阵(Distributed matrix)

6.4 基础统计

6.4.1摘要统计

示例代码如下:

6.4.2相关性

目前Spark支持两种相关性(correlations)系数:皮尔森相关系数(pearson)和斯皮尔曼等级相关系数(spearman)。下面通过示例说明相关系统的如何计算。

6.4.3假设检验

假设检验(Hypothesis testing),Spark MLlib目前支持皮尔森卡方检测(Pearson’s chi-squared tests),包括适配度检测和独立性检测。适配度检测要求输入为Vector, 独立性检验要求输入是Matrix。
代码示例:

6.4.4随机数据生成

代码示例:

6.5 RDD、Dataframe和Dataset

目前,spark.mllib包中基于RDD的APIs已进入维护模式,以后将以spark.ml包中的基于DataFrame的API为主。

6.5.1RDD

RDD是Spark建立之初的核心API。它是一种有容错机制的特殊集合, RDD是不可变分布式弹性数据集,在Spark集群中可跨节点分区,以函数式编程操作集合的方式,进行各种并行操作,提供分布式low-level API来操作,包括transformation和action等。

6.5.2Dataset/DataFrame

DataFrame与RDD相同之处,都是不可变分布式弹性数据集。不同之处在于,DataFrame多了数据的结构信息,即schema,类似于传统数据库中的表。

6.5.3相互转换

RDD、DataFrame和Dataset间可以互相转换。

6.6小结

本章主要介绍了Spark MLlib的一些内容,包括MLlib的生态、架构等内容,同时介绍了Spark MLlib算法底层依赖的基础内容,如数据类型、基础统计等,最后简单介绍了RDD、DataFrame与Dataset间的异同等。后续章节我们将通过一些实例,说明如何把前几章介绍的一些方法应用到具体实例中。


本章数据集下载

第5章 模型选择和优化

本章主要介绍如何使用Spark ML提供的方法及自定义函数等方法来对模型进行调优。我们可以通过Spark ML内建的交叉验证、训练验证拆方法、网格参数等方法进行模型调优,当然也可以自定义函数进行模型优化。
本章主要内容包括:
 模型选择
 交叉验证
 训练验证拆分法
 自定义函数调优

5.1 模型选择

调优可以是对单个的Estimator,比如LogisticRegression,或者对包含多个算法、特征化和其他步骤的整个Pipline。用户可以一次性对整个Pipline进行调优,而不必对Pipline中的每一个元素进行单独的调优。
MLlib支持使用像交叉验证(CrossValidator)和训练验证拆分法(TrainValidationSplit)这样的工具进行模型选择(Model selection)。这些工具需要以下的组件:
 Estimator:用户调优的算法或Pipline
 ParamMap集合:提供参数选择,有时也叫作用户查找的“参数网格”
 Evaluator:衡量模型在测试数据上的拟合程度
在上层,这些模型选择工具的工作方式如下:
 将输入数据切分成训练数据集和测试数据集
 对于每一个(训练数据,测试数据)对,通过ParamMap集合进行迭代:对于每个ParamMap,使用它提供的参数对Estimator进行拟合,给出拟合模型,然后使用Evaluator来评估模型的性能。
 选择表现最好的参数集合生成的模型。

5.2交叉验证

下例使CrossValidator从整个网格的参数中选择合适的参数,而从自动选择最优模型。
在整个参数网格中进行交叉验证是比较耗时的。例如,在下面的例子中,参数网格有3个hashingTF.numFeatures值和2个lr.regParam值,CrossValidator使用2折切分数据。最终将有(3 * 2) * 2 = 12个不同的模型将被训练。在真实场景中,很可能使用更多的参数和进行更多折切分(k=3和k=10都很常见)。使用CrossValidator的代价可能会异常的高,当大数据集比较大时,需要慎重选择。不过采用交叉验证法,对比手动调优,还是有较大优势。
下面通过示例说明如何使用CrossValidator从整个网格的参数中选择合适的参数。
导入必要的包:

配置一个流水线,该流水线包含3个stages: tokenizer, hashingTF, and lr。

使用ParamGridBuilder构造一个parameter grid

流水线,嵌入到CrossValidator实例中,这样流水线的任务都可使用网格参数。
CrossValidator一般需要一个Estimator, 参数集及一个评估器Evaluator。
BinaryClassificationEvaluator缺省的评估指标为AUC(areaUnderROC)。

通过交叉验证模型, 并获取最优参数集,并测试模型

查看最佳模型中各参数值

5.3训练验证拆分法

像CrossValidator一样,TrainValidationSplit最终适合使用最好的ParamMap
和整个数据集的Estimator。

5.4自定义模型选择

the best model was trained with rank = 20 and lambda = 0.1, and numIter = 10, and its RMSE on the test set is 1.0059139723438042.

5.5小结

本章主要介绍了几种模型选择或调优的方法,我们可以从训练的数据集入手,可以从模型参数入手,当然也可把两者结合起来。实际上模型的优化还有很多其他方法,如使用不同的算法、集成算法等等。下一章我们将介绍Spark MLlib一些基础知识,包括Spark MLlib架构、原理、算法及算法依赖的一些库、向量和矩阵等相关内容。

第2章 概率与信息论

本章讨论概率论和信息论,概率论是用于表示不确定性陈述的数学框架,即它是对事物不确定性的度量。
在人工智能领域,我们主要以两种方式来使用概率论。首先,概率法则告诉我们AI系统应该如何推理,所以我们设计一些算法来计算或者近似由概率论导出的表达式。其次,我们可以用概率和统计从理论上分析我们提出的AI系统的行为。
计算机科学的许多分支处理的对象都是完全确定的实体,但机器学习却大量使用概率论。实际上如果你了解机器学习的工作原理你就会觉得这个很正常。因为机器学习大部分时候处理的都是不确定量或随机量。
概率论和信息论是众多科学学科和工程学科的基础,也是机器学习、深度学习的重要基础。
如果你对概率论和信息论很熟悉了,可以跳过这章。如果你觉得这些内容还不够,还想进一步了解相关知识,可以参考相关专业教材。

2.1为何要概率、信息论

机器学习、深度学习需要借助概率、信息论?
要回答这个问题,我觉得至少应该了解以下两个问题:
(1)概率、信息论的主要任务;
(2)机器学习、深度学习与概率、信息论有哪些因缘。
概率研究对象不是预习知道或确定的事情,而是预习不确定或随机事件。研究这些不确定或随机事件背后规律或规则。或许有人会说,这些不确定或随机事件有啥好研究?他们本来就不确定或随机的,飘忽不定、不可捉摸。表面上看起来确实如此,有句话说得好:偶然中有必然,必然中有偶然。就拿我们比较熟悉微积分来说吧,如果单看有限的几步,很多问题都杂乱无章,还难处理,但是一旦加上一个无穷大(∞)这个“照妖镜”,其背后规律立显、原来难处理的也好处理了。概率研究的对象也类似。如大数定律、各种分布等等。
信息论主要研究对一个信号包含信息的多少进行量化。它的基本思想是一个不太可能的事件居然发生了,其提供的信息量要比一个非常可能发生的事件更多。这个看起来,也好像与我们的直觉相矛盾。
说起机器学习、深度学习与概率、信息论的因缘可就多了:
(1)被建模系统内在的随机性。例如一个假想的纸牌游戏,在这个游戏中我们假设纸牌被真正混洗成了随机顺序。
(2不完全观测。即使是确定的系统,当我们不能观测到所有驱动系统行为的所有变量或因素时,该系统也会呈现随机性。
(3)不完全建模。例如,假设我们制作了一个机器人,它可以准确地观察周围每一个对象的位置。 在对这些对象将来的位置进行预测时,如果机器人采用的是离散化的空间,那么离散化的方法将使得机器人无法确定对象们的精确位置:因为每个对象都可能处于它被观测到的离散单元的任何一个角落。也就是说,当不完全建模时,我们不能明确的确定结果,这个时候的不确定,就需要借助概率来处理。
由此看来,概率、信息论很重要,机器学习、深度学习确实很需要它们。后续我们可以看到很多实例,见证概率、信息论在机器学习、深度学习中是如何发挥它们作用的。

2.2样本空间与随机变量

样本空间
样本空间是一个实验或随机试验所有可能结果的集合,而随机试验中的每个可能结果称为样本点。例如,如果抛掷一枚硬币,那么样本空间就是集合{正面,反面}。如果投掷一个骰子,那么样本空间就是 {1,2,3,4,5,6}。
随机变量
随机变量,顾名思义,就是“其值随机而定”的变量,一个随机试验有许多可能结果,到底出现哪个预先是不知道的,其结果只有等到试验完成后,才能确定。如掷骰子,掷出的点数X是一个随机变量,它可以取1,2,3,4,5,6中的任何一个,到底是哪一个,要等掷了骰子以后才知道。因此,随机变量又是试验结果的函数,它为每一个试验结果分配一个值。比如,在一次扔硬币事件中,如果把获得的背面的次数作为随机变量X,则X可以取两个值,分别是0和1。如果随机变量X的取值是有限的或者是可数无穷尽的值,如:

2.3概率分布

概率分布用来描述随机变量(含随机向量)在每一个可能取到的状态的可能性大小。概率分布的有不同方式,这取决于随机变量是离散的还是连续的。
对于随机变量X,其概率分布通常记为P(X=x),或X ~P(x),表示X服从概率分布P(x)。
概率分布描述了取单点值的可能性或概率,但在实际应用中,我们并不关心取某一值的概率,特别是对连续型随机变量,它在某点的概率都是0,这个后续章节将介绍。因此,我们通常比较关心随机变量落在某一区间的概率,为此,引入分布函数的概念。

2.3.1 离散型随机变量



例如,设X的概率分布由例1给出,则
F(2)=P(X≤2)=P(X=0)+P(X=l)=0.04+0.32=0.36
常见的离散随机变量的分布有:
(1)两点分布
若随机变量X只可能取0和1两个值,且它的分布列为P(X=1)=p,P(X = 0) = l − P 其中(0 < P < 1),则称X服从参数为p的两点分布,记作X~B(1, p)。其分布函数为

(2)二项分布
二项分布是最重要的离散概率分布之一,由瑞士数学家雅各布•伯努利(Jokab Bernoulli)所发展,一般用二项分布来计算概率的前提是,每次抽出样品后再放回去,并且只能有两种试验结果,比如黑球或红球,正品或次品等。二项分布指出,随机一次试验出现的概率如果为p,那么在n次试验中出现k次的概率为:

假设随机变量X满足二项分布,且知道n,p,k等参数,我们如何求出各种情况的概率值呢?方法比较多,这里介绍一种非常简单的方法,利用Python的scipy库可以非常简单,直接利用这个统计接口stats即可,具体如下:

(3)泊松(Poisson)分布
若随机变量X所有可能取值为0,1,2,…,它取各个值的概率为:

这里介绍了离散型随机变量的分布情况,如果X是连续型随机变量,其分布函数通常通过密度函数来描述,具体请看下一节

2.3.2 连续型随机变量

与离散型随机变量不同,连续型随机变量采用概率密度函数来描述变量的概率分布。如果一个函数f(x)是密度函数,满足以下三个性质,我们就称f(x)为概率密度函数。

图2-1 概率密度函数
对连续型随机变量在任意一点的概率处处为0。
假设有任意小的实数∆x,由于{X=x}⊂{x-∆x<X≤x},由式(2.1)分布函数的定义可得:

这个连续分布被称之为正态分布,或者高斯分布。其密度函数的曲线呈对称钟形,因此又被称之为钟形曲线,其中μ是平均值,σ是标准差(何为平均值、标准差后续我们会介绍)。正态分布是一种理想分布。
正态分布如何用Python实现呢?同样,我们可以借助其scipy库中stats来实现,非常方便。

正态分布的取值可以从负无穷到正无穷。这里我们为便于可视化,只取把X数据定义在[-6,6]之间,用stats.norm.pdf得到正态分布的概率密度函数。另外从图形可以看出,上面两图的均值u都是0,只是标准差(σ)不同,这就导致图像的离散程度不同,标准差大的更分散,个中原因,我们在介绍随机变量的数字特征时将进一步说明。

2.4边缘概率

对于多维随机变量,如二维随机变量(X,Y),假设其联合概率分布为F(x,y),我们经常遇到求其中一个随机变量的概率分布的情况。这种定义在子集上的概率分布称为边缘概率分布。
例如,假设有两个离散的随机变量X,Y,且知道P(X,Y),那么我们可以通过下面求和的方法,得到边缘概率P(X):

边缘概率如何计算呢?这里我们通过一个实例来说明。假设有两个离散型随机变量X,Y,其联合分布概率如下:
表1.1:X与Y的联合分布

如果我们要求P(Y=0)的边缘概率,根据式(2.7)可得:
P(Y=0)=P(X=1,Y=0)+P(X=2,Y=0)=0.05+0.28=0.33

2.5条件概率

上一节我们介绍了边缘概率,它是值多维随机变量一个子集(或分量)上的概率分布。对于含多个随机变量的事件中,经常遇到求某个事件在其它事件发生的概率,例如,在表1.1的分布中,假设我们要求当Y=0的条件下,求X=1的概率?这种概率叫作条件概率。条件概率如何求?我们先看一般情况。
设有两个随机变量X,Y,我们将把X=x,Y=y发生的条件概率记为P(Y=y|X=x),那么这个条件概率可以通过以下公式计算:

其中P(Y=0)是一个边缘概率,其值为:P(X=1,Y=0)+P(X=2,Y=0)=0.05+0.28=0.33
而P(X=1,Y=0)=0.05.故P(X=1|Y=0)=0.05/0.33=5/33

2.6条件概率的链式法则

条件概率的链式法则,又称为乘法法则,把式(2.10)变形,可得到条件概率的乘法法则:
P(X,Y)=P(X)xP(Y|X)                                                                                        (2.11)
根据式(2.11)可以推广到多维随机变量,如:
P(X,Y,Z)=P(Y,Z)xP(X|Y,Z)
而P(Y,Z)=P(Z)xP(Y|Z)
由此可得:P(X,Y,Z)=P(X|Y,Z)x P(Y|Z)xP(Z)                                               (2.12)
更多维的情况,以此类推。

2.7独立性及条件独立性

两个随机变量X,Y,如果它们的概率分布可以表示为两个因子的乘积,并一个因子只含x,另一个因子只含y,那么我们就称这两个随机变量互相独立。这句话可能不好理解,我们换一种方式的来表达。或许更好理解。
如果对 ∀x∈X,y∈Y,P(X=x,Y=y)=P(X=x)P(Y=y) 成立,那么随机变量X,Y互相独立。
在机器学习中,随机变量为互相独立的情况非常普遍,一旦互相独立,联合分布的计算就变得非常简单。
这是不带条件的随机变量的独立性定义,如果两个随机变量带有条件,如P(X,Y|Z),它的独立性如何定义呢?这个与上面的定义类似。具体定义如下:
如果对∀x∈X,y∈Y,z∈Z,P(X=x,Y=y|Z=z)=P(X=x|Z=z)P(Y=y|Z=z) 成立
那么随机变量X,Y在给定随机变量Z时是条件独立的。
为便于表达,如果随机变量X,Y互相独立,又可记为X⊥Y,如果随机变量X,Y在给定时互相独立,则可记为X⊥Y|Z。
以上主要介绍离散型随机变量的独立性和条件独立性,如果是连续型随机变量,我们只要把概率换成随机变量的密度函数即可。

2.8期望、方差、协方差

在机器学习、深度学习中经常需要分析随机变量的数据特征及随机变量间的关系等,对于这些指标的衡量在概率统计中有相关的内容,如用来衡量随机变量的取值大小的期望(Expectation)值或平均值、衡量随机变量数据离散程度的方差(Variance)、揭示随机向量间关系的协调方差(Convariance)等。
这些衡量指标的定义及公式就是本节主要内容。
首先我们看随机变量的数学期望的定义:
对离散型随机变量X,设其分布律为:


期望有一些重要性质,具体如下:
设a,b为一个常数,X和Y是两个随机变量。则有:
(1)E(a)=a
(2)E(aX)=aE(X)
(3)E(aX+bY)=aE(X)+bE(Y)                                                                      (2.19)
(4)当X和Y相互独立时,则有:
E(XY)=E(X)E(Y)                                                                                      (2.20)
数学期望也常称为均值,即随机变量取值的平均值之意,当然这个平均,是指以概率为权的加权平均。期望值可大致描述数据的大小,但无法描述数据的离散程度,这里我们介绍一种刻画随机变量在其中心位置附近离散程度的数字特征,即方差。如何定义方差?
假设随机向量X有均值E(X)=a。试验中,X取的值当然不一定恰好是a,可能会有所偏离。偏离的量X-a本身也是一个随机变量。如果我们用X-a来刻画随机变量X的离散程度,当然不能取X-a的均值,因E(X-a)=0 ,说明正负偏离抵消了,当然我们可以取|X-a|这样可以防止正负抵消的情况,但绝对值在实际运算时很不方便。人们就考虑另一种方法,先对X-a平方以便消去符号,然后再取平均得


方差的平方根被称为标准差。
对于多维随机向量,如二维随机向量(X,Y)如何刻画这些分量间的关系?显然均值、方差都无能为力。这里我们引入协方差的定义,我们知道方差是X-EX乘以X-EX的均值,如果我们把其中一个换成Y-EY,就得到E(X-EX)(Y-EY),其形式接近方差,又有X,Y两者的参与,由此得出协方差的定义,随机变量X,Y的协方差,记为:Cov(X,Y)
Cov(X,Y) = E(X-EX)(Y-EY)                                                                             (2.22)
协方差的另一种表达方式:
Cov(X,Y) = E(XY)-EX×EY                                                                               (2.23)
方差可以用来衡量随机变量与均值的偏离程度或随机变量取值的离散度,而协方差则可衡量随机变量间的相关性强度,如果X与Y独立,那么它们的协方差为0。注意反之,并不一定成立,独立性比协方差为0的条件更强。不过如果随机变量X、Y都是正态分布,此时独立和协方差为0是一个概念。
当协方差为正时,表示随机变量X、Y为正相关;如果协方差为负,表示随机变量X、Y为负相关。
为了更好的衡量随机变量间的相关性,我们一般使用相关系数来衡量,相关系数将每个变量的贡献进行归一化,使其只衡量变量的相关性而不受各变量尺寸大小的影响,相关系统的计算公式如下:

求随机变量的方差、协方差、相关系统等,使用Python的numpy相关的函数,如用numpy.var求方差,numpy.cov求协方差,使用numpy.corrcoef求相关系数,比较简单,这里就不展开来说。
在机器学习中多维随机向量,通常以矩阵的方式出现,所以求随机变量间的线性相关性,就转换为求矩阵中列或行的线性相关性。这里我们举一个简单实例,来说明如果分析向量间的线性相关性并可视化结果。这个例子中使用的随机向量(或特征值)共有三个,一个是气温(temp),一个体感温度(atemp),一个是标签(label)说明共享单车每日出租量,以下是这三个特征的部分数据:

这里使用Python中数据分析库pandas及画图库matplotlib 、sns等。

从以上图可以看出,特征temp与atemp是线性相关的,其分布接近正态分布。

2.9贝叶斯定理

贝叶斯定理是概率论中的一个定理,它跟随机变量的条件概率以及边缘概率分布有关。在有些关于概率的解释中,贝叶斯定理(贝叶斯公式)能够告知我们如何利用新证据修改已有的看法。这个名称来自于托马斯•贝叶斯。
通常,事件A在事件B(发生)的条件下的概率,与事件B在事件A(发生)的条件下的概率是不一样的;然而,这两者是有确定的关系的,贝叶斯定理就是这种关系的陈述。贝叶斯公式的一个用途在于通过已知的三个概率函数推出第四个。
贝叶斯公式为:

2.10信息论

信息论是应用数学的一个分支,主要研究的是对信号所含信息的多少进行量化。它的基本想法是一个不太可能的事件居然发生了,要比一个非常可能的事件发生能提供更多的信息。本节主要介绍度量信息的几种常用指标,如信息量、信息熵、条件熵、互信息、交叉熵等。

2.10.1 信息量

1948年克劳德•香农(Claude Shannon)发表的论文“通信的数学理论”是世界上首次将通讯过程建立了数学模型的论文,这篇论文和1949年发表的另一篇论文一起奠定了现代信息论的基础。信息量是信息论中度量信息多少的一个物理量。它从量上反应具有确定概率的事件发生时所传递的信息。香农把信息看作是“一种消除不确定性”的量,而概率正好是表示随机事件发生的可能性大小的一个量,因此,可以用概率来定量地描述信息。
在实际运用中,信息量常用概率的负对数来表示,即,。为此,可能有不少人会问,为何用对数,前面还要带上负号?
用对数表示是为了计算方便。因为直接用概率表示,在求多条信息总共包含的信息量时,要用乘法,而对数可以变求积为求和。另外,随机事件的概率总是小于1,而真实小于1的对数为负的,概率的对数之前冠以负号,其值便成为正数。所以通过消去不确定性,获取的信息量总是正的。

2.10.2 信息熵

信息熵(entropy)又简称为熵,是对随机变量不确定性的度量。熵的概念由鲁道夫•克劳修斯(Rudolf Clausius)于1850年提出,并应用在热力学中。1948年,克劳德•艾尔伍德•香农(Claude Elwood Shannon)第一次将熵的概念引入信息论中,因此它又称为香农熵。
用熵来评价整个随机变量X平均的信息量,而平均最好的量度就是随机变量的期望,即熵的定义如下:

我们利用Python具体实现以下概率p与H(X)的关系:

从这个图形可以看出,当概率为0或1时,H(X)为0,说明此时随机变量没有不确定性,当p=0.5时,随机变量的不确定性最大,即信息量最大。H(X)此时取最大值。

2.10.3 条件熵

设二维随机变量(X,Y),其联合概率分布为:

注意,这个条件熵,不是指随机变量X在给定某个数的情况下,另一个变量的熵是多少,变量的不确定性是多少?而是期望!因为条件熵中X也是一个变量,意思是在一个变量X的条件下(变量X的每个值都会取),另一个变量Y熵对X的期望。
条件熵比熵多了一些背景知识,按理说条件熵的不确定性小于熵的不确定,即H(Y|X)≤H(Y),事实也是如此,下面这个定理有力地说明了这一点。
定理:对二维随机变量(X,Y),条件熵H(Y|X)和信息熵H(Y)满足如下关系:
H(Y|X)≤H(Y)                                                                                                   (2.29)

2.10.4 互信息

互信息(mutual information)又称为信息增益,用来评价一个事件的出现对于另一个事件的出现所贡献的信息量。记为:
I(X,Y)=H(Y)-H(Y|X)                                                                               (2.30)
在决策树的特征选择中,信息增益为主要依据。在给定训练数据集D,假设数据集由n维特征构成,构建决策树时,一个核心问题就是选择哪个特征来划分数据集,使得划分后的纯度最大,一般而言,信息增益越大,意味着使用使用某属性a来划分所得“纯度提升”越大。因此,我们常用信息增益来进行决策树划分属性。
2.10.5 相对熵
相对熵(relative entropy),所谓相对,一般是在两个随机变量之间来说,又被称为KL散度(Kullback–Leibler divergence,KLD),这里我们假设 p(x) 和 q(x) 是 X 取值的两个概率分布,如p(x)表示X的真实分布,q(x)表示X的训练分布或预测分布。则 p 对 q 的相对熵为:

相对熵有些重要性质:
(1)相对熵不是传统意义上的距离,它没有对称性,即
KL(p(x)||q(x))≠KL(q(x)||p(x))
(2)当预测分布q(x)与真实分布p(x)完全相等时,相对熵为0;
(3)如果两个分别差异越大,那么相对熵也越大;反之,如果两个分布差异越小,相对熵也越小。
(4)相对熵满足非负性,即 KL(p(x)||q(x))≥0

2.10.5 交叉熵

交叉熵可在神经网络(机器学习)中作为损失函数,p表示真实标记的分布,q则为训练后的模型的预测标记分布,交叉熵损失函数可以衡量p与q的相似性。交叉熵作为损失函数还有一个好处是使用sigmoid函数在梯度下降时能避免均方误差损失函数学习速率降低的问题,因为学习速率可以被输出的误差所控制。

第1章 线性代数

线性代数是数学的一个重要分支,广泛应用于科学和工程领域。线性代数,特别是矩阵运算是很多机器学习算法,尤其是深度学习的基础。因此,我们先介绍一些必备的线性代数的知识。
在深度学习的图像处理中,1张图由28*28像素点构成,而这28*28就是一个矩阵;深度学习中神经网络中,权重一般都是矩阵,我们经常把权重矩阵W与输入X相乘,输入X一般是向量,这就涉及矩阵与向量相乘的问题。诸如此类,矩阵及矩阵运算在深度学习中非常普遍,当然也非常重要。

1.1标量、向量、矩阵和张量

在线性代数和机器学习中,通常会遇到以下4种类型的数据。
标量(scalar):
一个标量就是一个单独的数,一般用小写的的变量名称表示,如a,x等
向量(vector):

我们可以把向量看作空间中的点,每个元素是不同的坐标轴上的坐标。
向量可以这样表示,那我们如何用编程语言如python来实现呢?如何表示一个向量?如何获取向量中每个元素呢?请看如下示例:

打印结果如下:
5
1 2 4 8

说明这个向量,元素个数为5,向量中索引一般从0开始,如a[0]表示第一个元素1,a[1]
表示第一个元素2, a[2]表示第3个元素4,依次类推。这个从左到右的排列顺序,如果从右到左,我们可用负数来表示,如a[-1]表示第1个元素(注:从右到左),a[-2]表示第2个元素,依次类推。
矩阵(matrix)

我们如何用Python来表示或创建矩阵呢?如果我们希望获取其中某个元素,该如何实现呢?请看如下示例:

打印结果:
[[1 2 3]
[4 5 6]]
6
(2, 3)
1 2 5
[4 5 6]

矩阵我们可以用嵌套向量生成,和向量一样,在numpy中,矩阵的元素的下标索引也是从从开始的。

张量(tensor)
几何代数中定义的张量是基于向量和矩阵的推广,通俗一点理解的话,我们可以将标量视为零阶张量,向量视为一阶张量,那么矩阵就是二阶张量,当然,三阶的就称为三阶张量,以此类推。在机器学习、深度学习中经常遇到多维矩阵,如一张彩色图片就是一个三阶张量,三个维度分别是图片的高度、宽度和色彩数据。
张量(tensor)也是深度学习框架Tensorflow的重要概念。Tensorflow实际上有tensor(张量)+flow(流)构成。
同样我们可以用python来生成张量及获取其中某个元素或部分元素,请看示例:

打印结果如下:
[[[ 0 1 2 3]
[ 4 5 6 7]]

[[ 8 9 10 11]
[12 13 14 15]]]
16
(2, 2, 4)
0 1 5
[4 5 6 7]

转置(transpose)


numpy如何实现转置?很简单,利用张量的T属性即可,示例如下:

打印结果如下:
[[1 2 3]
[4 5 6]]
[[1 4]
[2 5]
[3 6]]

1.2矩阵和向量

矩阵加法和乘法是矩阵运算中最常用的操作之一,两个矩阵相加,需要它们的形状相同,则是对应元素的相加,如:C=A+B,其中。矩阵也可以和向量相加,只要它们的列数相同,相加的结果是矩阵每行与向量相加,这种隐式地复制向量b到很多位置的方式称为广播(broadcasting),以下我们通过一个代码示例来说明。

打印结果为:
[[11 22 33]
[14 25 36]]

两个矩阵相加,要求它们的形状相同,如果两个矩阵相乘,如A和B相乘,结果为矩阵C,矩阵A和B需要什么条件呢?条件比较简单,只要矩阵A的列数和矩阵B的行数相同即可。如果矩阵A的形状为mxn,矩阵B的形状为nxp,那么矩阵C的形状就是mxp,例如
C=AB,则它们的具体乘法操作定义为:

1.3特殊矩阵与向量

有些特殊类型的矩阵在机器学习或深度学习中经常遇到,如对角矩阵、对称矩阵、单位矩阵、正交矩阵、可逆矩阵等等。向量由正交向量。下面我们逐一进行简要说明。
(1)可逆矩阵

后续我们有更详细的讨论及代码实现。
(2)对角矩阵

从上面两个式子可以看到对角矩阵的简洁高效。
(3)对称矩阵
对称矩阵,对于任意一个n阶方阵A,若A满足: 成立, 则称方阵A为对称矩阵。

1.4线性相关性及向量空间

前面我们介绍了向量、矩阵等概念,接下来我们将介绍向量组、线性组合、线性相关性、秩等重要概念。
由多个同维度的列向量构成的集合称为向量组,矩阵可以可以看成是有行向量或列向量构成的向量组。
1)线性组合

3)向量组的秩


秩是一个重要概念,运行非常广泛,实际上矩阵我们可以看成是一个向量组。如果把矩阵看成是由所有行向量构成的向量组,这样矩阵的行秩就等于行向量组的秩;如果把矩阵看成是由所有列向量构成的向量组,这样矩阵的列秩就等于列向量组的秩。
矩阵的行秩与列秩相等,因此,把矩阵的行秩和列秩统称为矩阵的秩。

1.5范数

数有大小,向量也有大小,向量的大小我们通过范数(Norm)来衡量。范数在机器学习、深度学习中运行非常广泛,特别在限制模型复杂度、提升模型的泛化能力方面效果不错。
p范数的定义如下:


以上说了向量一种度量方式,即通过范数来度量向量或矩阵的大小,并有具体公式,在实际编程中如何计算向量的范数呢?这里我们还是以Python为例进行说明。

打印结果如下:
[ 0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9]
4.5
1.68819430161
0.9

看来利用python求向量的范数还是很方便的。

3.6特征值分解

许多数学对象可以分解成多个组成部分。特征分解就是使用最广的矩阵分解之一,即将矩阵分解成一组特征向量和特征值。本节讨论的矩阵都是方阵。
我们先介绍特征值、特征向量的概念。
设A是一个n阶方阵,如果存在实数⋋和n维的非零向量x,满足:
Ax=⋋x                                                     (3.13)
那么把数⋋称为方阵A的特征值,向量x称为矩阵A对应特征值⋋的特征向量。

注意,并不是所有方阵都能进行特征值分解,一个n阶方阵A能进行特征值分解的充分必要条件是它含有n个线性无关的特征向量。
这里我们介绍了给定一个方阵,如何求该方阵的特征向量和特征值的理论方法,这些方法如何用编程语言来实现呢?实现起来是否麻烦?有了Python的帮忙,实现起来非常简单,具体请看如下示例:

打印结果:
[-0.37228132 5.37228132]
[-0.37228132 5.37228132]
[[-0.82456484 -0.41597356]
[ 0.56576746 -0.90937671]]
【说明】
在numpy.linalg模块中:
eigvals() 计算矩阵的特征值
eig() 返回包含特征值和对应特征向量的元组

1.7奇异值分解

上节我们介绍了方阵的一种分解方式,如果矩阵不是方阵,是否能分解?如果能,该如何分解?这节我们介绍一种一般矩阵的分解方法,称为奇异值分解,这种方法应用非常广泛,如降维、推荐系统、数据压缩等等。
矩阵非常重要,所以其分解方法也非常重要,方法也比较多,除了特征分解法,还有一种分解矩阵的方法,被称为奇异值分解(SVD)。将矩阵分解为奇异向量和奇异值。通过奇异分解,我们会得到一些类似于特征分解的信息。然而,奇异分解有更广泛的应用。
每个实数矩阵都有一个奇异值分解,但不一定都有特征分解。例如,非方阵的矩阵没有特征分解,这时我们只能使用奇异值分解。
奇异分解与特征分解类似,只不过这回我们将矩阵A分解成三个矩阵的乘积:
                                                          (1.15)
假设A是一个mxn矩阵,那么U是一个mxm矩阵,D是一个mxn矩阵,V是一个nxn矩阵。这些矩阵每一个都拥有特殊的结构,其中U和V都是正交矩阵,D是对角矩阵(注意,D不一定是方阵)。对角矩阵D对角线上的元素被称为矩阵A的奇异值。矩阵U的列向量被称为左奇异向量,矩阵V 的列向量被称右奇异向量。
SVD最有用的一个性质可能是拓展矩阵求逆到非方矩阵上。
奇异值分解,看起来太复杂,但用python来实现,却非常简单,这得感谢哪些一直致力于Python广大贡献者们。具体请看如下示例:

打印结果:
[ 1.09824632e+01 8.79229347e+00 1.03974857e+00 1.18321522e-15
2.13044868e-32]
[[ 10.98246322 0. 0. ]
[ 0. 8.79229347 0. ]
[ 0. 0. 1.03974857]]

1.8迹运算

迹运算返回的是矩阵对角元素的和:

利用python的numpy对矩阵求迹同样方便。请看以下示例。

打印结果:
15
15
171
171

1.9实例:主成分分析

主成分分析(Principal Component Analysis,PCA), 是一种统计方法。通过正交变换将一组可能存在相关性的变量转换为一组线性不相关的变量,转换后的这组变量叫主成分。
在许多机器学习、深度学习的应用中,往往需要处理大量样本或大的矩阵,多变量大样本无疑会为研究和应用提供了丰富的信息,但也在一定程度上增加了数据采集的工作量,更重要的是在多数情况下,许多变量之间可能存在相关性,从而增加了问题分析的复杂性,同时对分析带来不便。如果分别对每个指标进行分析,分析往往是孤立的,而不是综合的。盲目减少指标会损失很多信息,容易产生错误的结论。
因此需要找到一个合理有效的方法,在减少需要分析的指标或维度的同时,尽量减少原指标包含信息的损失,以达到对所收集数据进行全面分析的目的。由于各变量间存在一定的相关关系,因此有可能用较少的综合指标分别综合存在于各变量中的各类信息。主成分分析就属于这类降维的方法。
如何实现以上目标呢?这里我们简要说明一下原理,然后使用Python来实现,至于详细的推导过程,大家可参考相关书籍或网上资料。


以下我们Python具体实现一个PCA实例。
以iris为数据集,该数据集可以通过load_iris自动下载。
1)iris数据集简介:
Iris数据集是常用的分类实验数据集,由Fisher, 1936收集整理。Iris也称鸢尾花卉数据集,是一类多重变量分析的数据集。数据集包含150个数据集,分为3类,每类50个数据,每个数据包含4个属性。可通过花萼长度,花萼宽度,花瓣长度,花瓣宽度4个属性预测鸢尾花卉属于(Setosa,Versicolour,Virginica)三个种类中的哪一类。
2)算法主要步骤如下:
(1)对向量X进行去中心化
(2)计算向量X的协方差矩阵,自由度可以选择0或者1
(3)计算协方差矩阵的特征值和特征向量
(4)选取最大的k个特征值及其特征向量
(5)用X与特征向量相乘
3)代码实现

4)我们看一下各特征值的贡献率

从这个图形显示,前2个特征值的方差贡献率超过96%,所以k取2是合理的。

1.10小结

本章主要介绍了线性代数中矩阵及向量有关概念及相关规则和运算,线性代数是理解深度学习所必须掌握的基础数学学科之一。另一门在机器学习中无处不在的重要数学学科是概率论,我们将在下一章介绍。