年度归档:2018年

本文将介绍如何使用 Keras + Tensorflow1.6,python为3.6 创建卷积神经网络,并对其进行训练,以使得车辆保持在两条白线之间,如下图:

下载数据

备注:由于下载经常出现超时情况,大家可以先下载该文件,然后放到指定目录。

步骤1:获取驾驶数据

数据集由 ~7900 个图像和手动开车时收集的转向角组成。大约三分之二的图像与线之间的汽车。另外三分之一的车开始偏离航线,并且驶回线路之间。
数据集由 2 个 pickled 数组组成。X 是图像阵列,Y 是相应转向角度的阵列。

打印结果
X.shape: (7892, 120, 160, 3)
Y.shape: (7892,)

步骤2:拆分数据

在这里,对数据进行洗牌(shuffle),并将数据分成三部分。训练数据将用于训练我们的驾驶模型,使用验证数据避免过度拟合模型,测试数据用于测试我们的模型。

打印结果
7892

打印结果
7892

步骤3:增强训练数据

为了加倍我们的训练数据并防止转向偏差,我们翻转每个图像和转向角并将其添加到数据集中。还有其他的方法来增加使用翻译和假阴影驾驶数据,但我没有使用这些自动驾驶仪。

打印结果
12626

步骤4:创建驾驶模式

这种驾驶模式将是一个端到端的神经网络,接受图像阵列作为输入,并输出-90(左)和90(右)之间的转向角。 要做到这一点,我们将使用一个完全连接图层的3层卷积网络。该模型基于 Otavio 的 Carputer,但不产生油门值输出,不使用过去的转向值作为模型的输入,并且使用较少的卷积层。

使用 TensorFlow 后端。
查看目前image数据格式,是tensorflow格式(channels_last) 还是theano格式(channels_first)

'channels_last'

如果不是tensorflow格式,可以通过K.set_image_data_format('channels_last')进行修改,或修改~/.keras/keras.json文件。 前者只对当前会话有效,后者修改,将永久有效。

打印结果
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
img_in (InputLayer) (None, 120, 160, 3) 0
_________________________________________________________________
conv2d_1 (Conv2D) (None, 118, 158, 8) 224
_________________________________________________________________
activation_1 (Activation) (None, 118, 158, 8) 0
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 59, 79, 8) 0
_________________________________________________________________
conv2d_2 (Conv2D) (None, 57, 77, 16) 1168
_________________________________________________________________
activation_2 (Activation) (None, 57, 77, 16) 0
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 28, 38, 16) 0
_________________________________________________________________
conv2d_3 (Conv2D) (None, 26, 36, 32) 4640
_________________________________________________________________
activation_3 (Activation) (None, 26, 36, 32) 0
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 13, 18, 32) 0
_________________________________________________________________
flatten_1 (Flatten) (None, 7488) 0
_________________________________________________________________
dense_1 (Dense) (None, 256) 1917184
_________________________________________________________________
activation_4 (Activation) (None, 256) 0
_________________________________________________________________
dropout_1 (Dropout) (None, 256) 0
_________________________________________________________________
angle_out (Dense) (None, 1) 257
=================================================================
Total params: 1,923,473
Trainable params: 1,923,473
Non-trainable params: 0

步骤5:训练模型

我们已经学会了很难的方法,即使这一切都是完美的,如果你没有正确地训练,你的自动驾驶仪将无法工作。我遇到的最大的问题是过度适应模型,以至于在很少的情况下都不能正常工作。 这里是 2 个 Keras回调,将节省您的时间。 警告 - 如果仅使用CPU,则需要较长时间,这里我采用GPU进行训练

因时间关系,这里只进行 10 次迭代(epochs),训练时间2分钟左右,比较快。

运行结果
Train on 12626 samples, validate on 789 samples
Epoch 1/10
12608/12626 [============================>.]12608/12626 [============================>.] - ETA: 0s - loss: 41288.4184
Epoch 00001: val_loss improved from inf to 656.89127, saving model to /home/wumg/best_autopilot.hdf5
12626/12626 [==============================]12626/12626 [==============================] - 22s 2ms/step - loss: 41230.2161 - val_loss: 656.8913

Epoch 2/10
12608/12626 [============================>.]12608/12626 [============================>.] - ETA: 0s - loss: 550.5565
Epoch 00002: val_loss improved from 656.89127 to 543.14232, saving model to /home/wumg/best_autopilot.hdf5
12626/12626 [==============================]12626/12626 [==============================] - 15s 1ms/step - loss: 550.1622 - val_loss: 543.1423

Epoch 3/10
12608/12626 [============================>.]12608/12626 [============================>.] - ETA: 0s - loss: 476.8377
Epoch 00003: val_loss improved from 543.14232 to 417.43844, saving model to /home/wumg/best_autopilot.hdf5
12626/12626 [==============================]12626/12626 [==============================] - 15s 1ms/step - loss: 476.6054 - val_loss: 417.4384

Epoch 4/10
12608/12626 [============================>.]12608/12626 [============================>.] - ETA: 0s - loss: 420.1108
Epoch 00004: val_loss improved from 417.43844 to 416.97928, saving model to /home/wumg/best_autopilot.hdf5
12626/12626 [==============================]12626/12626 [==============================] - 15s 1ms/step - loss: 420.0584 - val_loss: 416.9793

Epoch 5/10
12608/12626 [============================>.]12608/12626 [============================>.] - ETA: 0s - loss: 399.4089
Epoch 00005: val_loss did not improve
12626/12626 [==============================]12626/12626 [==============================] - 15s 1ms/step - loss: 399.0109 - val_loss: 420.7120

Epoch 6/10
12608/12626 [============================>.]12608/12626 [============================>.] - ETA: 0s - loss: 373.5299
Epoch 00006: val_loss improved from 416.97928 to 411.31800, saving model to /home/wumg/best_autopilot.hdf5
12626/12626 [==============================]12626/12626 [==============================] - 15s 1ms/step - loss: 373.7999 - val_loss: 411.3180

Epoch 7/10
12608/12626 [============================>.]12608/12626 [============================>.] - ETA: 0s - loss: 356.3707
Epoch 00007: val_loss improved from 411.31800 to 384.65238, saving model to /home/wumg/best_autopilot.hdf5
12626/12626 [==============================]12626/12626 [==============================] - 15s 1ms/step - loss: 356.4031 - val_loss: 384.6524

Epoch 8/10
12608/12626 [============================>.]12608/12626 [============================>.] - ETA: 0s - loss: 338.6780
Epoch 00008: val_loss did not improve
12626/12626 [==============================]12626/12626 [==============================] - 15s 1ms/step - loss: 338.4380 - val_loss: 435.1508

Epoch 9/10
12608/12626 [============================>.]12608/12626 [============================>.] - ETA: 0s - loss: 325.1715
Epoch 00009: val_loss improved from 384.65238 to 348.49219, saving model to /home/wumg/best_autopilot.hdf5
12626/12626 [==============================]12626/12626 [==============================] - 15s 1ms/step - loss: 325.0649 - val_loss: 348.4922

Epoch 10/10
12608/12626 [============================>.]12608/12626 [============================>.] - ETA: 0s - loss: 304.1439
Epoch 00010: val_loss did not improve
12626/12626 [==============================]12626/12626 [==============================] - 15s 1ms/step - loss: 304.2825 - val_loss: 349.3760

步骤6:评估性能

我们可以通过绘制预测值和实际值来检查我们的模型预测是否合理。第一个图表显示我们的测试数据中存在一个学习关系(在训练期间,部分测试数据我们的模型没有注意到)。

第二张图,使用包含训练数据的非混洗(unshuffled)数据,来显示预测角度紧跟实际转向角度。

后续可进一步完善

改善模型,这个模型是纯粹(navie)的,因为它不使用过去的值来帮助预测未来。我们可以通过将过去的转向角度作为模型的输入来进行试验,添加一个递归层,或者只是改变卷积层的结构。
添加更多数据,随着我们添加更多驾驶数据,此模型将会得到改进。 预测油门,输出目前自动驾驶仪只能转向并保持恒定的速度。一个更复杂的模型将加速在直路上,并在路缘之前放缓。


机器学习、深度学习有三块基石:线性代数、概率与信息论、数值分析。线性代数上章已介绍,数值分析后续将介绍,三块基石缺一块都会地动山摇。本章讨论概率和信息论,概率是用于表示不确定性陈述的数学框架,即它是对事物不确定性的度量,而信息论主要研究信号或随机变量所包含的信息量。
在人工智能领域,概率法则告诉我们AI系统应该如何推理,概率和统计从理论上分析我们提出的AI系统的行为。
计算机科学的许多分支处理的对象都是完全确定的实体,但机器学习却大量使用概率。如果你了解机器学习的工作原理,或许你有更深的体会。因为机器学习大部分时候处理的都是不确定量或随机量。
概率论和信息论是众多学科的基础,也是机器学习、深度学习的重要基础。
如果你对概率论和信息论很熟悉了,可以跳过这章。如果你觉得本章内容还不够,希望进一步了解更多,可以参考相关专业教材。本章主要内容包括:

  • 为何要学概率与信息论
  • 样本空间与随机变量
  • 概率分布
  • 边缘概率
  • 条件概率
  • 期望、方差及协方差
  • 贝叶斯定理
  • 信息论

4.1为何要学概率、信息论

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

4.2样本空间与随机变量

随机试验中,每一个可能的结果,在试验中发生与否,都带有随机性,所以称为随机事件。而所有可能结果构成的全体,称为样本空间。随机变量、样本空间这两个概念非常重要,以下就这两个概念作进一步说明。
样本空间
样本空间是一个实验或随机试验所有可能结果的集合,随机试验中的每个可能结果称为样本点。例如,如果抛掷一枚硬币,那么样本空间就是集合{正面,反面}。如果投掷一个骰子,那么样本空间就是{1,2,3,4,5,6}。
随机变量
随机变量,顾名思义,就是“其值随机而定”的变量,一个随机试验有许多可能结果,到底出现哪个预先是不知道的,其结果只有等到试验完成后,才能确定。如掷骰子,掷出的点数X是一个随机变量,它可以取1,2,3,4,5,6中的任何一个,到底是哪一个,要等掷了骰子以后才知道。因此,随机变量又是试验结果的函数,它为每一个试验结果分配一个值。比如,在一次扔硬币事件中,如果把获得的背面的次数作为随机变量X,则X可以取两个值,分别是0和1。如果随机变量X的取值是有限的或者是可数无穷尽的值,如:
则称 X为离散随机变量。如果X由全部实数或者由一部分区间组成,如:
X={x| a≤x≤b},其中a<b,它们都为实数。
则称 X为连续随机变量,连续随机变量的取值是不可数及无穷尽的。
有些随机现象需要同时用多个随机变量来描述。例如对地面目标射击,弹着点的位置需要两个坐标(X,Y)才能确定,X,Y都是随机变量,而(X,Y)称为一个二维随机变量或二维随机向量,多维随机向量含义依次类推。

4.3概率分布

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

4.3.1 离散型随机变量

设x_1,x_2,…,x_n是随机变量X的所有可能取值,对每个取值x_i,X = x_i是其样本空间S上的一个事件,为描述随机变量X,还需知道这些事件发生的可能性(概率)。
设离散型随机变量X的所有可能取值为x_i (i=1,2,…,n)。
P(X = x_i) = P_i,i= 1,2,...n
称之为X的概率分布或分布律,也称概率函数。
常用表格形式来表示X的概率分布:


例1:某篮球运动员投中篮圈的概率是0.8,求他两次独立投篮投中次数X的概率分布。

且P(X=0)+P(X=1)+P(X=2)=0.04+0.32+0.64=1
于是随机变量X的概率分布可表示为:
X         0      1         2
P_i 0.04    0.32    0.64
若已知一个离散型随机变量X的概率分布:

例如,设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等参数,我们如何求出各种情况的概率值呢?方法比较多,这里介绍一种比较简单的方法,利用scipy库的统计接口stats即可,具体如下:

运行后的二项分布图如图4-1所示。

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

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

4.3.2 连续型随机变量

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

图4-2 概率密度函数
对连续型随机变量在任意一点的概率处处为0。

最常见的正态分布的密度函数为:

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

sigmal系统与正态分布如图4-3所示。


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

4.4边缘概率

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

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

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

4.5条件概率

上一节我们介绍了边缘概率,它是多维随机变量一个子集(或分量)上的概率分布。对于含多个随机变量的事件中,经常遇到求某个事件在其他事件发生的概率,例如,在表4-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
式(4.10)为离散型随机变量的条件概率,对连续型随机变量也有类似公式。假设(X,Y)为二维连续型随机变量,它们的密度函数为f(x,y),关于Y的边缘概率密度函数为f_Y (y),且满足f_Y (y)>0,假设

在X=x的条件下,关于Y的条件分布函数为:

4.6条件概率的链式法则

条件概率的链式法则,又称为乘法法则,把式(4.10)变形,可得到条件概率的乘法法则:
P(X,Y)=P(X)×P(Y|X)                                                                   (4.16)
根据式(4.16)可以推广到多维随机变量,如:
P(X,Y,Z)=P(Y,Z) ×P(X|Y,Z)
而P(Y,Z)=P(Z) ×P(Y|Z)
由此可得:P(X,Y,Z)=P(X|Y,Z) ×P(Y|Z) ×P(Z)                          (4.17)
推广到n维随机变量的情况,可得:

4.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。
以上主要介绍离散型随机变量的独立性和条件独立性,如果是连续型随机变量,我们只要把概率换成随机变量的密度函数即可。

4.8期望、方差及协方差

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

如果是随机变量函数,如随机变量X的g(x)的期望,公式与式(4.21)或式(4.22)类似,只要把x换成g(x)即可,即随机变量函数g(x)的期望为:

期望有一些重要性质,具体如下:
设a,b为一个常数,X和Y是两个随机变量。则有:
(1)E(a)=a
(2)E(aX)=aE(X)
(3)E(aX+bY)=aE(X)+bE(Y) (4.25)
(4)当X和Y相互独立时,则有:
E(XY)=E(X)E(Y) (4.26)
数学期望也常称为均值,即随机变量取值的平均值之意,当然这个平均,是指以概率为权的加权平均。期望值可大致描述数据的大小,但无法描述数据的离散程度,这里我们介绍一种刻画随机变量在其中心位置附近离散程度的数字特征,即方差。如何定义方差?
假设随机向量X有均值E(X)=a。试验中,X取的值当然不一定恰好是a,可能会有所偏离。偏离的量X-a本身也是一个随机变量。如果我们用X-a来刻画随机变量X的离散程度,当然不能取X-a的均值,因E(X-a)=0 ,说明正负偏离抵消了,当然我们可以取|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)                                    (4.28)
协方差的另一种表达方式:
Cov(X,Y) =E(XY)-EX×EY                                       (4.29)
方差可以用来衡量随机变量与均值的偏离程度或随机变量取值的离散度,而协方差则可衡量随机变量间的相关性强度,如果X与Y独立,那么它们的协方差为0。反之,并不一定成立,独立性比协方差为0的条件更强。不过如果随机变量X、Y都是正态分布,此时独立和协方差为0是一个概念。
当协方差为正时,表示随机变量X、Y为正相关;如果协方差为负,表示随机变量X、Y为负相关。
为了更好的衡量随机变量间的相关性,我们一般使用相关系数来衡量,相关系数将每个变量的贡献进行归一化,使其只衡量变量的相关性而不受各变量尺寸大小的影响,相关系统的计算公式如下:
ρ_xy=(Cov(X,Y))/(√(Var(X)) √(Var(Y)))                                             (4.30)
由式(4.30)可知,相关系统是在协方差的基础上进行了正则化,从而把相关系数的值限制在[-1,1]之间。如果ρ_xy=1,说明随机变量X、Y是线性相关的,即可表示为Y=kX+b,其中k,b为任意实数,且k>0;如果ρ_xy=-1,说明随机变量X、Y是负线性相关的,即可表示为Y=-kX+b,其中k>0。
上面我们主要以两个随机变量为例,实际上协方差可以推广到n个随机变量的情况或n维的随机向量。对n维的随机向量,我们可以的第一个nxn的协方差矩阵,而且满足:

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

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

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

图4-4 特征分布及相关性

4.9贝叶斯定理

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

  • P(B|A)是已知A发生后B的条件概率,也由于得自A的取值而被称作B的后验概率。
  • P(B)是B的先验概率(或边缘概率)。之所以称为"先验"是因为它不考虑任何A方面的因素。
  • P(A|B)是已知B发生后A的条件概率,也称为似然(likelihood),也由于得自B的取值而被称作A的后验概率。
  • P(A)是A的先验概率或边缘概率。

4.10信息论

信息论是应用数学的一个分支,主要研究的是对信号所含信息的多少进行量化。它的基本想法是一个不太可能的事件居然发生了,要比一个非常可能的事件发生能提供更多的信息。本节主要介绍度量信息的几种常用指标,如信息量、信息熵、条件熵、互信息、交叉熵等。
1.信息量
1948年克劳德•香农(Claude Shannon)发表的论文“通信的数学理论”是世界上首次将通讯过程建立了数学模型的论文,这篇论文和1949年发表的另一篇论文一起奠定了现代信息论的基础。信息量是信息论中度量信息多少的一个物理量,它从量上反应具有确定概率的事件发生时所传递的信息。香农把信息看作是“一种消除不确定性”的量,而概率正好是表示随机事件发生的可能性大小的一个量,因此,可以用概率来定量地描述信息。
在实际运用中,信息量常用概率的负对数来表示,即,。为此,可能有人会问,为何要用对数,前面还要带上负号?
用对数表示是为了计算方便。因为直接用概率表示,在求多条信息总共包含的信息量时,要用乘法,而对数可以变求积为求和。另外,随机事件的概率总是小于1,而真实小于1的对数为负的,概率的对数之前冠以负号,其值便成为正数。所以通过消去不确定性,获取的信息量总是正的。
2.信息熵
信息熵(entropy)又简称为熵,是对随机变量不确定性的度量。熵的概念由鲁道夫•克劳修斯(Rudolf Clausius)于1850年提出,并应用在热力学中。1948年,克劳德•艾尔伍德•香农(Claude Elwood Shannon)第一次将熵的概念引入信息论中,因此它又称为香农熵。
用熵来评价整个随机变量X平均的信息量,而平均最好的量度就是随机变量的期望,即熵的定义如下:

信息熵越大,包含的信息就越多,那么随机变量的不确定性就越大。
以下我们通过一个实例进一步说明这个关系。
假设随机变量X服从0-1分布,其概率分布为:
P(X=1)=p,P(X=0)=1-p
这时,X的熵为:
H(X)=-p log_2(p)-(1-p) log_2(1-p)
我们利用Python具体实现以下概率p与H(X)的关系:

图4-5 概率与信息熵
从这个图形可以看出,当概率为0或1时,H(X)为0,说明此时随机变量没有不确定性,当p=0.5时,随机变量的不确定性最大,即信息量最大。H(X)此时取最大值。
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)                                                                                      (4.36)
4.互信息
互信息(mutual information)又称为信息增益,用来评价一个事件的出现对于另一个事件的出现所贡献的信息量。记为:
I(X,Y)=H(Y)-H(Y|X)                                                                               (2.37)
在决策树的特征选择中,信息增益为主要依据。在给定训练数据集D,假设数据集由n维特征构成,构建决策树时,一个核心问题就是选择哪个特征来划分数据集,使得划分后的纯度最大,一般而言,信息增益越大,意味着使用某属性a来划分所得“纯度提升”越大。因此,我们常用信息增益来构建决策树划分属性。
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
6.交叉熵
交叉熵可在神经网络(机器学习)中作为代价函数,p表示真实标记的分布,q则为训练后的模型的预测标记分布,交叉熵代价函数可以衡量p与q的相似性。交叉熵作为代价函数还有一个好处是使用sigmoid函数在梯度下降时能避免均方误差代价函数学习速率降低的问题,因为学习速率可以被输出的误差所控制。

4.11 小结

概率与信息论是机器学习的重要基础及重要理论依据。本章介绍了概率论、信息论的一些基本概念,如样本空间、随机变量等。根据随机变量取值不同,又可分为有离型和连续型随机变量;根据随机变量的维度又可分为一维或多维随机变量。概率分布、边缘分布是刻画随机变量的重要特征,而期望、方差及协方差是随机变量三个常用统计量。信息论是刻画随机变量的另一种方式,信息论在深度学习、人工智能中应用非常广泛,后续章节也经常会出现。

下载本章代码及数据


机器学习、深度学习的基础除了编程语言外,还有一个就是应用数学。它一般包括线性代数、概率与信息论、概率图、数值计算与最优化等。其中线性代数又是基础的基础。线性代数是数学的一个重要分支,广泛应用于科学和工程领域。大数据、人工智能的源数据在模型训练前,都需要转换为向量或矩阵,而这些运算正是线性代数的主要内容。
如在深度学习的图像处理中,如果1张图由28*28像素点构成,那这28*28就是一个矩阵。在深度学习的神经网络中,权重一般都是矩阵,我们经常把权重矩阵W与输入X相乘,输入X一般是向量,这就涉及矩阵与向量相乘的问题。诸如此类,向量或矩阵之间的运算在深度学习中非常普遍,也非常重要。
本章主要介绍如下内容:
标量、向量、矩阵和张量
矩阵和向量运算
特殊矩阵与向量
线性相关性及向量空间

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

在机器学习、深度学习中,首先遇到的就是数据,如果按类别来划分,我们通常会遇到以下4种类型的数据。
1.标量(scalar)
一个标量就是一个单独的数,一般用小写的变量名称表示,如a,x等。
2.向量(vector)
向量就是一列数或一个一维数组,这些数是有序排列的。通过次序中的索引,我们可以确定向量中每个单独的数。通常我们赋予向量粗体的小写变量名称,如x、y等。一个向量一般有很多元素,这些元素如何表示?我们一般通过带脚标的斜体表示,如x_1 表示向量x中的第一个元素,x_2表示第二元素,依次类推。
当需要明确表示向量中的元素时,我们一般将元素排列成一个方括号包围的纵列:

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

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

这说明向量元素个数为5,向量中索引一般从0开始,如a[0]表示第一个元素1,a[1]
表示第二个元素2,a[2]表示第三个元素4,依次类推。这是从左到右的排列顺序,如果从右到左,我们可用负数来表示,如a[-1]表示第1个元素(注:从右到左),a[-2]表示第2个元素,依次类推。
3.矩阵(matrix)
矩阵是二维数组,其中的每一个元素被两个索引而非一个所确定。我们通常会赋予矩阵粗体的大写变量名称,比如A。如果一个实数矩阵高度为m,宽度为n,那么我们说A∈R^mxn。
与向量类似,可以通过给定行和列的下标表示矩阵中元素,下标用逗号分隔,如A_1,1表示A左上的元素,A_1,2表示第一行第二列对应的元素,依次类推;这是表示单个元素,如果我们想表示1列或1行,该如何表示呢?我们可以引入冒号":"来表示,如第1行,可用A1,:表示,第2行,用A2,:表示,第1列用A:,1表示,第n列用A:,n表示。
如何用Python来表示或创建矩阵呢?如果希望获取其中某个元素,该如何实现呢?请看如下实例:

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

矩阵可以用嵌套向量生成,和向量一样,在Numpy中,矩阵元素的下标索引也是从0开始的。
4.张量(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]
5.转置(transpose)
转置以主对角线(左上到右下)为轴进行镜像操作,通俗一点来说就是行列互换。将矩

向量可以看作只有一列的矩阵,把(3.1)式中向量x进行转置,得到下式。

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

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

3.2矩阵和向量运算

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

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

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

矩阵乘积有很多重要性质,如满足分配律A(B+C)=AB+AC 和结合律,A(BC)=(AB)C。大家思考一下是否满足交换律?

3.3特殊矩阵与向量

上一节我们介绍了一般矩阵的运算,实际上在机器学习或深度学习中,我们还经常遇到一些特殊类型的矩阵,如可逆矩阵、对称矩阵、对角矩阵、单位矩阵、正交矩阵等等。这些特殊矩阵有特殊属性,下面我们逐一进行说明。
1.可逆矩阵
先简单介绍一下可逆矩阵,因后续需要用到。在(3.3)式中,假设矩阵W已知,向量b已知,如何求向量x?为求解向量x,我们需要引入一个称为逆矩阵的概念。而为了求逆矩阵,又牵涉到单位矩阵,何为单位矩阵?单位矩阵的结构很简单,就是所有沿主对角线上的元素都是1,而其他位置的元素都是0的方阵(行数等于列数的矩阵),一般记为I_n,如:


对此后续我们有更详细的讨论及代码实现。
2.对角矩阵
对角矩阵只有在主对角线上才有非零元素,其余都是0。从形式上来看,如果A为对角矩阵,当且仅当对所有i≠j,A_(i,j)=0。对角矩阵可以是方阵(行数等于列数)也可以不是方阵,如下矩阵,就是一个对角矩阵。

对角矩阵有非常好的性质,这些性质使很多计算非常高效,在机器学习、深度学习中经常会遇到对角矩阵。
对于对角矩阵为方阵的情况,我们可以把对角矩阵简单记为:

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

3.4线性相关性及向量空间

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

2.线性相关

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

3.5范数

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


前面主要介绍了利用范数来度量向量的大小,矩阵的大小如何度量呢?我们可以用类似的方法。在深度学习中,常用Frobenius范数来描述,即:

其中θ表示x与y之间的夹角。
以上说了向量一种度量方式,即通过范数来度量向量或矩阵的大小,并有具体公式,在实际编程中如何计算向量的范数呢?这里我们还是以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.14)
那么把数⋋称为方阵A的特征值,向量x称为矩阵A对应特征值⋋的特征向量。
假设矩阵A有n个线性无关的特征向量{ν^1,ν^2,⋯,ν^n},它们对应的特征值为{⋋_1,⋋_2,⋯,⋋_n}
把这n个线性无关的特征向量组合成一个新方阵,每一列是一个特征向量。
V=[ν^1,ν^2,⋯,ν^n]
用特征值构成一个n阶对角矩阵,对角线的元素都是特征值。
〖〖diag(λ)=[⋋〗_1,⋋_2,⋯,⋋_n]〗^T
那么,A的特征分解可表示为:
A=Vdiag(λ)V^(-1)                                   (3.15)
注意,并不是所有方阵都能进行特征值分解,一个n阶方阵A能进行特征值分解的充分必要条件是它含有n个线性无关的特征向量。
这里我们介绍了给定一个方阵,如何求该方阵的特征向量和特征值?如何用编程语言实现?这些问题有了Python的帮忙,实现起来都非常简单,具体请看如下示例:

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

3.7奇异值分解

上节我们介绍了方阵的一种分解方式,如果矩阵不是方阵,是否能分解?如果能,该如何分解?这节我们介绍一种一般矩阵的分解方法,称为奇异值分解,这种方法应用非常广泛,如降维、推荐系统、数据压缩等等。
矩阵非常重要,所以其分解方法也非常重要,方法也比较多,除了特征分解法,还有一种分解矩阵的方法,被称为奇异值分解(SVD)。将矩阵分解为奇异向量和奇异值。通过奇异分解,我们会得到一些类似于特征分解的信息。然而,奇异分解有更广泛的应用。
每个实数矩阵都有一个奇异值分解,但不一定都有特征分解。例如,非方阵的矩阵就没有特征分解,这时我们只能使用奇异值分解。
奇异分解与特征分解类似,只不过这回我们将矩阵A分解成三个矩阵的乘积:
A=UDV^T                                      (3.16)
假设A是一个m×n矩阵,那么U是一个m×m矩阵,D是一个m×n矩阵,V是一个n×n矩阵。这些矩阵每一个都拥有特殊的结构,其中U和V都是正交矩阵,D是对角矩阵(注意,D不一定是方阵)。对角矩阵D对角线上的元素被称为矩阵A的奇异值。矩阵U的列向量被称为左奇异向量,矩阵V 的列向量被称右奇异向量。
SVD最有用的一个性质可能是拓展矩阵求逆到非方矩阵上。奇异值分解,看起来很复杂,如果用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]]

3.8迹运算

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

迹运算在某些场合非常有用。若不使用求和符号,有些矩阵运算很难描述,而通过矩阵乘法和迹运算符号可以清楚地表示。例如,迹运算提供了另一种描述矩阵Frobenius 范数的方式:

对迹运算的表达式,我们可以使用很多等式来表示。例如,迹运算在转置运算下是不变的:

多个矩阵相乘得到的方阵的迹,和将这些矩阵中的最后一个挪到最前面之后相乘的迹是相同的。当然,我们需要考虑挪动之后矩阵乘积依然有定义:
Tr(ABC)=Tr(CAB)=Tr(BCA)
利用Python的Numpy对矩阵求迹同样方便。请看以下示例。

打印结果:
15
15
171
171

3.9实例:Python实现主成分分析

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

要使信息损失最小,一种合理的设想就是重构后的点X^*与原来的数据点之间距离最小,据此,PCA可转换为求带约束的最优化问题:

最后对(3.22)式两端对w求导,并令导数为0,化简后就可得到:
XX^T W=⋋W                                                                                                        (3.23)
由(3.23)式可知,W是由协方差矩阵XX^T的特征向量构成的特征矩阵,利用特征值分解的方法就可求出W。
以下我们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)代码实现

各特征值的贡献率如图3-1所示,可以看出,前2个特征值的方差贡献率超过95%,所以k取2有其合理性。

图3-1 各特征值的贡献率示意图

3.10小结

本章主要介绍线性代数中矩阵及向量有关概念,以及相关规则和运算等。线性代数是机器学习、深度学习的重要基础,与之相当的还有概率与信息论,我们将在下一章介绍。

本章代码下载


第1章我们介绍了NumPy,它是数据计算的基础,更是深度学习框架的基石。但如果直接使用NumPy计算大数据,其性能已成为一个瓶颈。
随着数据爆炸式增长,尤其是图像数据、音频数据等数据的快速增长,迫切需要突破这个瓶颈。需求就是强大动力!通过大咖们的不懈努力,在很多方面取得可喜进展,如硬件有GPU,软件有Theano、Keras、TensorFlow,算法有卷积神经网络、循环神经网络等等。
Theano是Python的一个库,为开源项目,在2008年,由Yoshua Bengio领导的加拿大蒙特利尔理工学院LISA实验室开发。对于解决大量数据的问题,使用Theano可能获得与手工用C实现差不多的性能。另外通过利用GPU,它能获得比CPU上的快很多数量级的性能。Theano开发者在2010年公布的测试报告中指出:在CPU上执行程序时,Theano程序性能是NumPy的1.8倍,而在GPU上是NumPy的11倍。这还是2010的测试结果,近些年无论是Theano还是GPU,性能都有显著提高。
这里我们把Theano作为基础来讲,除了其性能方面的跨越外,它还是“符合计算图”的开创者,当前很多优秀的开源工具,如TensorFlow、Keras等,都派生于或借鉴了Theano的底层设计。所以了解Theano的使用,将有助于我们更好学习TensorFlow、Keras等其他开源工具。
至于Theano是如何实现性能方面的跨越?如何用“符号计算图”来运算等,本章都将有所涉猎,但限于篇幅无法深入分析,只做一些基础性的介绍。涵盖的主要内容:
 如何安装Theano
 符号变量是什么
 如何设计符号计算图
 函数的功能
 共享变量的妙用

2.1安装

这里主要介绍Linux+Anaconda+theano环境的安装说明,Linux为CentOS或Ubuntu都可以,安装Python、NumPy、SciPy等,建议使用Anaconda来安装,当然也可用pip进行安装。最好使用工具来安装,这样可以避免很多程序依赖的麻烦,而且日后的软件升级维护也很方便。
Theano支持CPU、GPU,如果使用GPU还需要安装其驱动程序如CUDA等,限于篇幅这里只介绍CPU的(TensorFlow将介绍基于GPU的安装),有关GPU的安装,大家可参考:http://www.deeplearning.net/software/theano/install.html
以下为安装主要步骤:
(1)安装anaconda
从anaconda官网:https://www.anaconda.com/download/下载linux环境最新的软件包,Python版本建议选择3系列的,2系列后续将不再维护。下载文件为一个sh程序包:如:
Anaconda3-4.3.1-Linux-x86_64.sh,然后在下载目录下运行如下命令:

bash Anaconda3-4.3.1-Linux-x86_64.sh

安装过程中按enter或y即可,安装完成后,程序提示是否把anaconda的binary加入到.bashrc配置文件中,加入后运行python、ipython时将自动使用新安装的Python环境。
安装完成后,你可用conda list命令查看已安装的库:

conda list

安装成功的话,应该能看到numpy、scipy、matplotlib、conda等库。
(2)安装theano
利用conda 来安装或更新程序

conda install theano

(3)测试
先启动python,然后导入theano模块,如果不报错,说明安装成功。

$ python
Python 3.6.0 |Anaconda custom (64-bit)| (default, Dec 23 2016, 12:22:00)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import theano
>>>

2.2符号变量

存储数据需要用到各种变量,Theano如何使用变量的呢?Theano用一种变量类型称为符号变量来表示变量,用TensorVariable表示,又称为张量(Tensor),张量是Theano的核心元素(也是TensorFlow的核心元素),它是Theano表达式和运算操作的基本单位。张量可以是标量(scalar)、向量(vector)、矩阵(matrix)等的统称。具体来说,标量就是我们通常看到的0阶的张量,如12,a等,而向量和矩阵分别为1阶张量和2阶的张量。
如果通过这些概念,你还不很清楚,没有关系,可以结合以下一个实例,来直观感受一下。
首先定义三个标量:一个代表输入x、一个代表权重w、一个表示偏移量b,然后计算这些标量运算结果z=x*w+b,Theano代码实现如下:

打印结果
net_input: 6.500000

通过以上实例我们不难看出,Theano本身是一个通用的符号计算框架,与非符号架构的框架不同,它先使用tensor variable初始化变量,然后将复杂的符号表达式编译成为函数模型,最后运行时传入实际数据进行计算。整个过程涉及三个步骤:定义符号变量,编译代码,执行代码。这节主要介绍第一步如何定义符号变量,其他步骤将在后续小节介绍。
如何定义符号变量?或定义符号变量有哪些方式?在Theano中定义符号变量有大致三种:使用内置的变量类型、自定义变量类型、转换其他的变量类型,具体如下:
(1)使用内置的变量类型创建
目前Theano支持7种内置的变量类型,分别是标量(scalar)、向量(vector)、行(row)、列(col)、矩阵(matrix)、tensor3、tensor4等。其中标量是0阶张量,向量为1阶张量,矩阵为二阶张量等,以下为创建内置变量的实例:

其中,name指定变量名字,dtype指变量的数据类型。
(2)自定义变量类型
内置的变量类型只能处理4维及以下的变量,如果需要处理更高维的数据时,我们可以使用Theano的自定义变量类型,具体通过TensorType方法来实现:

其中broadcastable是True或False的布尔类型元组,元组的大小等于变量的维度,如果为True,表示变量在对应维度上的数据可以进行广播,否则数据不能广播。
广播机制(broadcast)是一种重要机制,有了这种机制,就可以方便对不同维的张量进行运算,否则,就要手工把低维数据变成高维,利用广播机制系统自动利用复制等方法把低维数据补齐,numpy也有这种机制。以下我们通过图1-2所示的一个实例来说明广播机制原理:

图2-1 广播机制

图2-1中矩阵与向量相加的具体代码如下:

(3) 将Python类型变量或者Numpy类型变量转化为Theano共享变量
共享变量是Theano实现变量更新的重要机制,后面我们会详细讲解。要创建一个共享变量,只要把一个Python对象或NumPy对象传递给shared函数即可,如下示例:

2.3符号计算图模型

符号变量定义后,需要说明这些变量间的运算关系,如何描述变量间的运算关系? Theano实际采用符号计算图模型来实现。首先创建表达式所需的变量,然后通过操作符(op)把这些变量结合在一起。如图2-1。
Theano处理符号表达式时通过把符号表达式转换为一个计算图(graph)来处理(TensorFlow也使用了这种方法,等到我们介绍TensorFlow时,大家可对比一下),符号计算图的节点有:variable、type、apply和op。
variable节点:即符号的变量节点,符号变量是符号表达式存放信息的数据结构,可以分为输入符号和输出符号。
type节点:当定义了一种具体的变量类型以及变量的数据类型时,Theano为其指定数据存储的限制条件。
apply节点:把某一种类型的符号操作符应用到具体的符号变量中,与variable不同,apply节点无须由用户指定,一个apply节点包括3个字段:op、inputs、outputs。
op节点:即操作符节点,定义了一种符号变量间的运算,如+、-、sum()、tanh()等。
Theano是将符号表达式的计算表示成graphs。这些graphs是由Apply 和 Variable将节点连接而组成,它们分别与函数的应用和数据相连接。 操作由 Op 实例表示,而数据类型由 Type 实例表示。下面这段代码和图2-2,说明了这些代码所构建的结构。借助这个图或许有助于您进一步理解,如何将这些内容拟合在一起:

图2-2 符号计算图
图2-2中箭头表示指向python对象的引用。这里的蓝色盒子是一个 Apply 节点,红色盒子是 Variable 节点,绿色圆圈是Ops,紫色盒子是 Types。
在创建 Variables 之后,应用 Apply Ops得到更多的变量,这些变量仅仅是一个占位符,在function中作为输入。变量指向 Apply 节点的过程是用来表示函数通过它们的owner 域来生成它们 。这些Apply节点是通过它们的inputs和outputs域来得到它们的输入和输出变量。
x 和 y 的owner 域的指向都是None,是因为它们不是另一个计算的结果。如果它们中的一个是另一个计算的结果,那么owner域将会指向另一个蓝色盒。

2.4函数

上节我们介绍了如何把一个符号表达式转化为符号计算图,这节我们介绍函数的功能,函数是Theano的一个核心设计模块,它提供一个接口,把函数计算图编译为可调用的函数对象。前面介绍了如何定义自变量x(不需要赋值),这节介绍如何编写函数方程。
(1)函数定义的格式
theano.function(inputs, outputs, mode=None, updates=None, givens=None, no_default_updates=False, accept_inplace=False, name=None,rebuild_strict=True, allow_input_downcast=None, profile=None, on_unused_input='raise')。
这里参数看起来很多,但常用的一般只用到三个,inputs表示自变量、outputs表示函数的因变量(也就是函数的返回值),还有另外一个比较常用的是updates这个参数,这个一般用于神经网络共享变量参数更新,通常以字典或元组列表的形式指定;givens是一个字典或元组列表,记为[(var1,var2)],表示在每一次函数调用时,在符号计算图中,把符号变量var1节点替换为var2节点,该参数常用来指定训练数据集的batch大小。
我们看一个有多个自变量、同时又有多个因变量的函数定义例子:

打印结果
[array(5.0, dtype=float32), array(6.0, dtype=float32)]
在执行theano.function()时,Theano进行了编译优化,得到一个end-to-end的函数,传入数据调用f(2,3)时,执行的是优化后保存在图结构中的模型,而不是我们写的那行z=x+y,尽管二者结果一样。这样的好处是Theano可以对函数f进行优化,提升速度;坏处是不方便开发和调试,由于实际执行的代码不是我们写的代码,所以无法设置断点进行调试,也无法直接观察执行时中间变量的值。
(2)自动求导
Theano有了符号计算图2-2,自动计算导数就很容易了。tensor.grad()唯一需要做的就是从outputs逆向遍历到输入节点。对于每个Op,它都定义了怎么根据输入计算出偏导数。使用链式法则就可以计算出梯度了。利用Theano求导时非常方便,可以直接利用函数theano.grad(),比如求S函数的导数:

以下代码实现当x=3的时候,求s函数的导数:

打印结果
0.045176658779382706
(3)更新共享变量参数
在深度学习中通常需要迭代多次,每次迭代都需要更新参数。Theano如何更新参数呢?在theano.function函数中,有个非常重要的参数updates,updates是一个包含两个元素的列表或tuple,updates=[old_w,new_w],当函数被调用的时候,这个会用new_w替换old_w,具体看下面这个例子。

打印结果:1、4
在求梯度下降的时候,经常用到updates这个参数。比如updates=[w,w-α*(dT/dw)],其中dT/dw就是我们梯度下降的时候,代价函数对参数w的偏导数,α是学习速率。为便于大家的更全面的了解Theano函数一些使用,下面我们通过一个逻辑回归的完整实例来说明:

2.5条件与循环

编写函数需要经常用到条件或循环,这节我们就简单介绍Theano如何实现条件判断或逻辑循环。 (1)条件判断 Theano是一种符号语言,条件判断不能直接使用Python的if语句。在Theano可以用ifelse 和 Switch来表示判定。这两个判决语句有何区别呢? switch对每个输出变量进行操作,ifelse只对一个满足条件的变量操作,比如: 对语句: switch(cond, ift, iff) 如果满足条件,则switch既执行ift也执行iff。对语句: if cond then ift else iff ifelse只执行ift或者只执行iff 以下通过一个示例进一步说明

打印结果
time spent evaluating both values 0.005268 sec
time spent evaluating one value 0.007501 sec

(2)循环语句
scan是theano中构建循环Graph的方法,scan是个灵活复杂的函数,任何用循环、递归或者跟序列有关的计算,都可以用scan完成。其格式如下:
theano.scan(fn, sequences=None, outputs_info=None, non_sequences=None, n_steps=None, truncate_gradient=-1, go_backwards=False, mode=None, name=None, profile=False, allow_gc=None, strict=False)
参数说明:
fn:函数类型,scan的一步执行。除了outputs_info,fn可以返回sequences变量的更新updates。fn的输入变量顺序为sequences中的变量,outputs_info的变量,non_sequences中的变量。如果使用了taps,则按照taps给fn喂变量。taps的详细介绍会在后面的例子中给出。
sequences:scan进行迭代的变量,scan会在T.arange()生成的list上遍历,例如下面的polynomial 例子。
outputs_info:初始化fn的输出变量,和输出的shape一致。如果初始化值设为None表示这个变量不需要初始值。
non_sequences:fn函数用到的其他变量,迭代过程中不可改变(unchange)。
n_steps:fn的迭代次数。
下面通过一个例子解释scan函数的具体使用方法。
代码实现思路是:先定义函数one_step,它就是scan里的fn,其任务就是计算多项式的一项,scan函数返回的result里会保存多项式每一项的值,然后我们对result求和,就得到了多项式的值。

打印结果
多项式各项的值: [ 2 30 400 6000 50000]
多项式和的值: 56432

2.6共享变量

共享变量(shared variable)是实现机器学习算法参数更新的重要机制。shared函数会返回共享变量。这种变量的值在多个函数可直接共享。可以用符号变量的地方都可以用共享变量。但不同的是,共享变量有一个内部状态的值,这个值可以被多个函数共享。它可以存储在显存中,利用GPU提高性能。我们可以使用get_value和set_value方法来读取或者修改共享变量的值,使用共享变量实现累加操作。

这里state是一个共享 变量,初始化为0,每次调用accumulator(),state都会加上inc。共享变量可以像普通张量一样用于符号表达式,另外,他还有自己的值,可以直接用.get_value()和.set_value()方法来访问和修改。
上述代码引入了函数中updates参数。updates参数是一个list,其中每个元素是一个元组(tuple),这个tuple的第一个元素是一个共享变量,第二个元素是一个新的表达式。updatas中的共享变量会在函数返回后更新自己的值。updates的作用在于执行效率,updates多数时候可以用原地(in-place)算法快速实现,在GPU上,Theano可以更好地控制何时何地给共享变量分配空间,带来性能提升。最常见的神经网络权值更新,一般会用update实现。

2.7小结

Theano基于NumPy,但性能方面又高于NumPy。因Theano采用了张量(Tensor)这个核心元素,在计算方面采用符号计算模型,而且采用共享变量、自动求导、利用GPU等适合于大数据、深度学习的方法,其他很多开发项目也深受这些技术和框架影响。本章主要为后续介绍TensorFlow做个铺垫。

本章代码下载

NumPy是Python基础,更是数据科学的通用语言,而且与TensorFlow关系密切,所以我们把它列为第一章。
NumPy为何如此重要?实际上Python本身含有列表(list)和数组(array),但对于大数据来说,这些结构有很多不足。因列表的元素可以是任何对象,因此列表中所保存的是对象的指针。这样为了保存一个简单的[1,2,3],都需要有3个指针和三个整数对象。对于数值运算来说这种结构显然比较浪费内存和CPU计算时间。 至于array对象,它直接保存数值,和C语言的一维数组比较类似。但是由于它不支持多维,也没有各种运算函数,因此也不适合做数值运算。
NumPy(Numerical Python 的简称)的诞生弥补了这些不足,NumPy提供了两种基本的对象:ndarray(N-dimensional array object)和 ufunc(universal function object)。ndarray是存储单一数据类型的多维数组,而ufunc则是能够对数组进行处理的函数。
NumPy的主要特点:
 ndarray,快速和节省空间的多维数组,提供数组化的算术运算和高级的广播功能。
 使用标准数学函数对整个数组的数据进行快速运算,而不需要编写循环。
 读取/写入磁盘上的阵列数据和操作存储器映像文件的工具。
 线性代数,随机数生成,和傅里叶变换的能力。
 集成C,C++,Fortran代码的工具。
在使用 NumPy 之前,需要先导入该模块:

本章主要内容如下:
 如何生成NumPy的ndarray的几种方式
 如何存取元素
 如何操作矩阵
 如何合并或拆分数据
 简介NumPy的通用函数
 简介NumPy的广播机制

1.1生成ndarray的几种方式

NumPy封装了一个新的数据类型ndarray(n-dimensional array),一个多维数组对象,该对象封装了许多常用的数学运算函数,方便我们做数据处理,以及数据分析,如何生成ndarray呢?这里我们介绍生成ndarray的几种方式,如从已有数据中创建;利用random创建;创建特殊多维数组;使用arange函数等。
1.从已有数据中创建
直接对python的基础数据类型(如列表、元组等)进行转换来生成ndarray。
(1)将列表变换成ndarray

打印结果
[ 3.14 2.17 0. 1. 2. ]
<class 'numpy.ndarray'>
(2)嵌套列表可以转换成多维ndarray

打印结果
[[ 3.14 2.17 0. 1. 2. ]
[ 1. 2. 3. 4. 5. ]]
<class 'numpy.ndarray'>
如果把(1)和(2)中的列表换成元组也同样适合。
2.利用random模块生成ndarray
在深度学习中,我们经常需要对一些变量进行初始化,适当的初始化能提高模型的性能。通常我们用随机数生成模块random来生成,当然random模块又分为多种函数:
random生成0到1之间的随机数;uniform生成均匀分布随机数;randn生成标准正态的随机数;normal生成正态分布;shuffle随机打乱顺序;seed设置随机数种子等等,以下我们列举几个简单示例。

打印结果
[[ 0.88900951 0.47818541 0.91813526]
[ 0.48329167 0.63730656 0.14301479]
[ 0.9843789 0.99257093 0.24003961]]
<class 'numpy.ndarray'>
生成一个随机种子,对生成的随机数打乱。

打印结果
[[-1.0856306 0.99734545 0.2829785 ]
[-1.50629471 -0.57860025 1.65143654]]
随机打乱后数据
[[-1.50629471 -0.57860025 1.65143654]
[-1.0856306 0.99734545 0.2829785 ]]
<class 'numpy.ndarray'>
3. 创建特定形状的多维数组
数据初始化时,有时需要生成一些特殊矩阵,如0或1的数组或矩阵,这时我们可以利用np.zeros,np.ones,np.diag来实现,以下我们通过几个示例来说明。

我们还可以把生成的数据保存到磁盘,然后从磁盘读取。

4.利用arange函数
arange是numpy模块中的函数,其格式为:arange([start,] stop[, step,], dtype=None)
根据start与stop指定的范围,以及step设定的步长,生成一个 ndarray,其中start缺省值为0,步长step可为小数。

1.2存取元素

上节我们介绍了生成ndarray的几种方法,数据生成后,如何读取我们需要的数据?这节我们介绍几种读取数据的方法。

如果你对上面这些获取方式还不是很清楚,没关系,下面我们通过图形的方式说明如何获取多维数组中的元素,如图1-1所示,左边的为表达式,右边为对应获取元素。

图1-1 获取多维数组中的元素
获取数组中的部分元素除通过指定索引标签外,还可以使用一些函数来实现,如通过random.choice函数可以从指定的样本中进行随机抽取数据。

打印结果:
随机可重复抽取
[[ 7. 22. 19. 21.]
[ 7. 5. 5. 5.]
[ 7. 9. 22. 12.]]
随机但不重复抽取
[[ 21. 9. 15. 4.]
[ 23. 2. 3. 7.]
[ 13. 5. 6. 1.]]
随机但按制度概率抽取
[[ 15. 19. 24. 8.]
[ 5. 22. 5. 14.]
[ 3. 22. 13. 17.]]

1.3矩阵操作

深度学习中经常涉及多维数组或矩阵的运算,正好NumPy模块提供了许多相关的计算方法,下面介绍一些常用的方法。

上面介绍的几种方法是numpy.linalg模块中的函数,numpy.linalg模块中的函数是满足行业标准级的Fortran库,具体请看表1-1。

表1-1 numpy.linalg中常用函数

1.4数据合并与展平

在机器学习或深度学习中,经常遇到需要把多个向量或矩阵按某轴方向进行合并,展平也经常使用,如在卷积或循环神经网络中,在全连接层之前,需要把矩阵展平。这节介绍几种数据合并和展平的方法。
(1)合并一维数组

打印结果:
[1 2 3 4 5 6]
[1 2 3 4 5 6]
(2)多维数组的合并

打印结果
[[0 1]
[2 3]
[0 1]
[2 3]]
合并后数据维度 (4, 2)
按列合并结果:
[[0 1 0 1]
[2 3 2 3]]
合并后数据维度 (2, 4)
(3)矩阵展平

打印结果
[[0 1 2]
[3 4 5]]
按列优先,展平
[0 3 1 4 2 5]
按行优先,展平
[0 1 2 3 4 5]

1.5通用函数

NumPy提供了两种基本的对象,即ndarray和ufunc对象。前面我们对ndarray作了简单介绍,本节将介绍它的另一个对象ufunc,ufunc(通用函数)是universal function的缩写,它是一种能对数组的每个元素进行操作的函数。许多ufunc函数都是在c语言级别实现的,因此它们的计算速度非常快。此外,功能比math模块中函数更灵活。math模块的输入一般是标量,但NumPy中函数可以是向量或矩阵,而利用向量或矩阵可以避免循环语句,这点在机器学习、深度学习中经常使用。表1-2为NumPy常用几个通用函数。
表1-2 NumPy几个常用通用函数

(1)使用math与numpy函数性能比较:

打印结果
math.sin: 0.5169950000000005
numpy.sin: 0.05381199999999886
由此可见,numpy.sin比math.sin快近10倍。

(2)使用循环与向量运算比较:
充分使用python的numpy库中的内建函数(built-in function),实现计算的向量化,可大大提高运行速度。Python的NumPy库中的内建函数使用了SIMD指令。例如下面所示在Python中使用向量化要比使用循环计算速度快得多。如果使用GPU,其性能将更强大,不过NumPy不提供GPU支持。TensorFlow提供GPU,第8章将介绍TensorFlow如何使用GPU来加速算法。

打印结果
dot = 250215.601995
for loop----- Computation time = 798.3389819999998ms
dot = 250215.601995
verctor version---- Computation time = 1.885051999999554ms
从程序运行结果上来看,该例子使用for循环运行时间是使用向量运算运行时间的约400倍。因此,深度学习算法中,一般都使用向量化矩阵运算。

1.6 广播机制

广播机制(Broadcasting)的功能是为了方便不同shape的array(numpy库的核心数据结构)进行数学运算。广播提供了一种向量化数组操作的方法,以便在C中而不是在Python中进行循环,这通常会带来更高效的算法实现。广播的兼容原则为:
(1)对齐尾部维度
(2)shape相等or其中shape元素中有一个为1
以下通过实例来具体说明。

打印结果
[ 0 2 4 6 8 10 12 14 16 18]
[ 3 4 5 6 7 8 9 10 11 12]
[ 0 1 4 9 16 25 36 49 64 81]
[[ 0 2]
[ 2 4]
[ 4 6]
[ 6 8]
[ 8 10]]

图1-2 NumPy多维数组相加
有时为了保证矩阵运算正确,我们可以使用reshape()函数来变更矩阵的维度。

1.7小结

本章简单介绍了NumPy模块的两个基本对象ndarray、ufunc,介绍了ndarray对象的几种生成方法,及如何存取其元素、如何操作矩阵或多维数组、如何进行数据合并与展平等。最后说明了通用函数及广播机制。如果想进一步了解NumPy,大家可参考:http://www.numpy.org/

本章代码下载

=====================================================
欢迎加入《Python深度学习基于TensorFlow》的QQ交流群:
在线答疑与交流!

=====================================================

本书代码及数据下载

目录
第一部分 Python及应用数学基础 12
第1章 NumPy常用操作
1.1生成ndarray的几种方式 12
1.2存取元素 15
1.3矩阵操作 17
1.4数据合并与展平 18
1.5通用函数 20
1.6 广播机制 22
1.7小结 23
第2章 Theano基础
2.1安装 24
2.2符号变量 25
2.3符号计算图模型 28
2.4函数 29
2.5条件与循环 32
2.6共享变量 35
2.7小结 36
第3章线性代数
3.1标量、向量、矩阵和张量 37
3.2矩阵和向量运算 40
3.3特殊矩阵与向量 41
3.4线性相关性及向量空间 42
3.5范数 43
3.6特征值分解 44
3.7奇异值分解 45
3.8迹运算 46
3.9实例:Python实现主成分分析 47
3.10小结 51
第4章概率与信息论
4.1为何要学概率、信息论 52
4.2样本空间与随机变量 53
4.3概率分布 53
4.3.1 离散型随机变量 54
4.3.2 连续型随机变量 56
4.4边缘概率 58
4.5条件概率 59
4.6条件概率的链式法则 60
4.7独立性及条件独立性 60
4.8期望、方差及协方差 61
4.9贝叶斯定理 63
4.10信息论 64
4.11 小结 67
第5章概率图模型 68
5.1为何要引入概率图 68
5.2使用图描述模型结构 68
5.3贝叶斯网络 70
5.3.1隐马尔可夫模型简介 70
5.3.2隐马尔可夫模型三要素 71
5.3.3隐马尔可夫模型三个基本问题 72
5.3.4隐马尔可夫模型简单实例 72
5.4马尔可夫网络 74
5.4.1 马尔可夫随机场 75
5.4.2条件随机场 76
5.4.3实例:用Tensorflow实现条件随机场 77
5.5小结 80
第二部分深度学习理论与应用 81
第6章机器学习基础
6.1监督学习 81
6.1.1 线性模型 82
6.1.2 SVM 85
6.1.3贝叶斯分类器 87
6.1.4 集成学习 89
6.2无监督学习 91
6.2.1主成分分析 91
6.2.2k-means聚类 92
6.3梯度下降与优化 93
6.3.1梯度下降简介 93
6.3.2梯度下降与数据集大小 94
6.3.3传统梯度优化的不足 96
6.3.4动量算法 97
6.3.5 自适应算法 99
6.3.6有约束最优化 102
6.4前馈神经网络 104
6.4.1神经元结构 104
6.4.2感知机的局限 105
6.4.4多层神经网络 106
6.4.5 实例:用TensorFlow实现XOR 109
6.4.6反向传播算法 111
6.5 实例:用keras构建深度学习架构 116
6.6小结 117
第7章深度学习挑战与策略 118
7.1 正则化 118
7.1.1正则化参数 118
7.1.2增加数据量 123
7.1.3梯度裁剪 123
7.1.4提前终止 124
7.1.5共享参数 124
7.1.6 Dropout 125
7.2 预处理 127
7.2.1 初始化 127
7.2.2 归一化 128
7.3批量化 128
7.3.1随机梯度下降法 128
7.3.2批标准化 129
7.4并行化 131
7.4.1TensorFlow利用GPU加速 131
7.4.2深度学习并行模式 132
7.5选择合适激活函数 134
7.6选择合适代价函数 135
7.7选择合适优化算法 137
7.8小结 137
第8章安装TensorFlow 138
8.1 TensorFlow CPU版的安装 138
8.2 TensorFlow GPU版的安装 139
8.3配置Jupyter Notebook 143
8.4实例:比较CPU与GPU性能 144
8.5 实例:单GPU与多GPU性能 145
8.6小结 147
第8章新增:TensorFlow-gpu 2.0升级问题及解决方法
第9章TensorFlow基础 148
9.1 TensorFlow系统架构 148
9.2数据流图 149
9.3 TensorFlow基本概念 150
9.3.1张量 151
9.3.2算子 152
9.3.3计算图 152
9.3.4会话 153
9.3.5常量 155
9.3.6变量 155
9.3.7占位符 160
9.3.8实例:比较constant 、variable和 placeholder 161
9.4TensorFlow实现数据流图 163
9.5可视化数据流图 164
9.6TensorFlow分布式 166
9.7小结 168
第10章TensorFlow图像处理 169
10.1加载图像 169
10.2图像格式 170
10.3把图像转换为TFRecord文件 171
10.4 读取TFRecord文件 172
10.5图像处理实例 174
10.6 全新的数据读取方式Dataset API 177
10.6.1 Dataset API 架构 177
10.6.2 构建Dataset 178
10.6.3创建迭代器 182
10.6.4从迭代器中获取数据 182
10.6.5读入输入数据 183
10.6.6预处理数据 183
10.6.7批处理数据集元素 184
10.6.8使用高级API 185
10.7 小结 186
第11章TensorFlow神经元函数 187
11.1激活函数 187
11.1.1sigmoid函数 187
11.1.2tanh函数 188
11.1.3relu函数 188
11.1.4softplus函数 190
11.1.5dropout函数 190
11.2代价函数 190
11.2.1sigmoid_cross_entropy_with_logits函数 191
11.2.2softmax_cross_entropy_with_logits函数 192
11.2.3sparse_softmax_cross_entropy_with_logits函数   193
11.2.4weighted_cross_entropy_with_logits函数 193
11.3小结 194
第12章 TensorFlow自编码器 195
12.1自编码简介 195
12.2降噪自编码 197
12.3 实例:TensorFlow实现自编码 197
12.4实例:用自编码预测信用卡欺诈 200
12.5小结 208
第 13章 TensorFlow 实现Word2Vec 209
13.1词向量及其表达 209
13.2Word2Vec原理 210
13.2.1 CBOW模型 210
13.2.2 Skim-gram模型 211
13.3实例:TensorFlow实现Word2Vec 212
13.4小结 218
第14章 TensorFlow 卷积神经网络 219
14.1 卷积神经网络简介 219
14.2卷积层 220
14.2.1 卷积核 221
14.2.2步幅 222
14.2.3 填充 223
14.2.4 多通道上的卷积 224
14.2.5激活函数 225
14.2.6卷积函数 225
14.3池化层 227
14.4归一化层 228
14.5TensorFlow实现简单卷积神经网络 229
14.6TensorFlow实现进阶卷积神经网络 230
14.7几种经典卷积神经网络 235
14.8小结 236
第15章TensorFlow 循环神经网络 237
15.1 循环神经网络简介 237
15.2前向传播与随时间反向传播 239
15.3 梯度消失或爆炸 242
15.4LSTM算法 242
15.5 RNN其他变种 245
15.6 RNN应用场景 247
15.7 实例:用LSTM实现分类 248
15.8小结 252
第 16章 TensorFlow高层封装 253
16.1 TensorFlow高层封装简介 253
16.2 Estimator简介 254
16.3 实例:使用Estimator预定义模型 255
16.4 实例:使用Estimator自定义模型 258
16.5 Keras简介 264
16.6实例:Keras实现序列式模型 265
16.7 TFLearn简介 267
16.7.1利用TFLearn解决线性回归问题 268
16.7.2利用TFLearn进行深度学习 269
16.8小结 270
第17章情感分析 271
17.1深度学习与自然语言处理 271
17.2词向量简介 272
17.3 循环神经网络 273
17.4 迁移学习简介 273
17.5实例:TensorFlow实现情感分析 274
17.5.1 导入数据 274
17.5.2定义辅助函数 280
17.5.3构建RNN模型 280
17.5.4调优超参数 283
17.5.5训练模型 283
17.6小结 286
第18章利用TensorFlow预测乳腺癌 287
18.1 数据说明 287
18.2数据预处理 288
18.3 探索数据 290
18.4 构建神经网络 294
18.5 训练并评估模型 296
18.6 小结 298
第19章聊天机器人 299
19.1 聊天机器人原理 299
19.2 带注意力的框架 300
19.3用TensorFlow实现聊天机器人 304
19.3.1 接口参数说明 305
19.3.2 训练模型 308
19.4 小结 321
第20 章人脸识别 322
20.1人脸识别简介 322
20.2项目概况 325
20.3实施步骤 325
20.3.1数据准备 326
20.3.2 预处理数据 326
20.3.3 训练模型 329
20.3.4 测试模型 334
20.4小结 337
第三部分扩展篇 338
第21章强化学习基础 338
21.1强化学习简介 338
21.2强化学习常用算法 340
21.2.1 Q-Learning算法 340
21.2.2 Sarsa算法 342
21.2.3 DQN算法 342
21.3小结 343
第22章生成式对抗网络 344
22.1 GAN简介 344
22.2GAN的改进版本 346
22.3小结 348
第23章语音识别基础 349
23.1 语言识别系统的架构 349
23.2 语音识别的原理 350
23.3 语音识别发展历程 354
23.4小结 355

第24章 语音识别基础

语音作为最自然便捷的交流方式,一直是人机通信和交互最重要的研究领域之一。自动语音识别(Automatic Speech Recognition,ASR)是实现人机交互尤为关键的技术,其所要解决的问题是让计算机能够“听懂”人类的语音,将语音中传化为文本,或其它语言的语音(如同声翻译)等。
自动语音识别技术起源于上世纪五十年代,经过几十年的发展已经取得了显著的成效。尤其是近些年,把深度学习融入其中,使ASR取得突飞猛进的发展。越来越多的语音识别智能软件和应用走入了人们的日常生活,比较典型的有苹果的Siri、亚马逊的Alexa、微软的小娜、科大讯飞的语音输入法、叮咚智能音箱等。

本章主要介绍语音识别的基本概念、发展轨迹及主要技术和原理等。包括:
 语音识别系统架构
 语音识别基础
 语音识别发展轨迹
 语音识别未来方向

24.1 语言识别系统的架构

图24.1为语言识别系统的典型结构,其中关键技术涉及信号处理、特征提取、解码器及声学模型和语言模型的建立等。

图24.1 语音识别系统的架构
 信号处理及特征提取
该模块的主要任务是从输入信号中提取特征,供声学模型处理。同时,它一般也包括了一些信号处理技术,以尽可能降低环境噪声、信道、说话人等因素对特征造成的影响。
 声学模型
声学模型充分利用了声学、语音学、环境特性以及说话人性别口音等信息,对语音进行建模。目前的语音识别系统往往采用隐含马尔科夫模型(Hidden Markov Model,HMM)建模,表示某一语音特征矢量序列对某一状态序列的后验概率。隐含马尔科夫模型是一种概率图模型,可以用来表示序列之间的相关关系,常常被用来对时序数据建模。
 词典
词典包含系统所能处理的词汇集及其发音。词典实际提供了声学模型建模单元与语言模型建模单元间的映射。
 语言模型
语言模型对系统所针对的语言进行建模。理论上,包括正则语言,上下文无关文法在内的各种语言模型都可以作为语言模型,但目前各种系统常用的还是基于统计的N 元文法(N-Gram),即统计前后 N 个字出现的概率。
 解码器
解码器是语音识别系统的核心之一,其任务是对输入的信号,根据声学、语言模型及词典,寻找能够以最大概率输出该信号的词序列。
这节我们介绍了语音识别系统的主要架构,接下了我们从基本概念入手,进一步分析语音识别系统如果把语音或声音一步步变成文字或语句的。

24.2 语音识别的原理

上节介绍了语音识别系统的架构,这节我们对上节内容进行细化或具体化,为了大家对语音识别系统有直观认识,主要介绍系统涉及的一般基本概念及其原理。
语音识别系统的输入是何种形式的文件?
语音识别系统的输入自然是语音或声音,我们知道声音实际上是一种波。常见的MP3等格式都是压缩格式,必须转成非压缩的纯波形文件来处理,比如通常看到wav文件。wav文件里存储的除了一个文件头以外,就是声音波形的一个个点了。下图24.2是一个波形的示例

图24.2 声音波形示意图
所以输入文件一般是wav文件。有了输入文件后,接下就需要文件进行预处理,一般需要做哪些预处理呢?
(1)去首尾静音
利用信号处理的一些技术,切除首尾端的静音,这种操作一般称为VAD,以降低对后续步骤造成的干扰。
(2)分帧
对声音分帧,也就是把声音切成一小段一小段,每小段称为一帧。分帧操作一般不是简单的切开,而是使用移动窗口函数来实现。帧与帧之间一般有交叠,如图24.3

图24.3 分帧示意图
图24.3中,每帧的长度为25毫秒,每两帧之间有25-10=15毫秒的交叠。我们称为以帧长25ms、帧移动10ms分帧。
(3)特征提取
通过分帧后,语音变成了很多小段。但这些小段波形计算机还是无法处理,因此需要将波形作变换,常用的一般变换方法是提取MFCC特征,依据人耳的生理特性,把每一帧波形变成一个多维向量,这个向量包含了这帧语音的内容信息,这个过程称为声学特征提取。
这样声音就成了12行(假设声学特征为12维)、N列的一个矩阵(N是总帧数),称为观察序列。观察序列如图24.4,每一帧都用一个12维的向量表示,色块的颜色深浅表示向量值的大小。至此,准备工作基本完成,接下就是训练模型了。

图24.4 观察序列
(4)训练模型
在开始训练模型前,我们先介绍两个基本概念:
音素:单词的发音由音素构成。对英语,一种常用的音素集是卡内基梅隆大学的一套 由39个音素构成的音素集。汉语一般直接用全部声母和韵母作为音素集,另外汉语识 别还分有调无调。
状态:这里理解成比音素更细致的语音单位就可。通常把一个音素划分成3个状态。
具体训练过程可分为三步来实现:
第一步,把帧识别成状态(难点);
第二步,把状态组合成音素;
第三步,把音素组合成单词。
首先看第一步,如何把帧识别成状态?如图24.5所示


图24.5 音素的合作过程
在图24.5中,每个小竖条代表一帧,若干帧语音对应一个状态,每三个状态组合成一个音素,若干个音素组合成一个单词。也就是说,只要知道每帧语音对应哪个状态,语音识别的结果也就出来了。如何由帧音素来确定对应的状态呢?有个容易想到的办法,看某帧对应哪个状态的概率最大,那这帧就属于哪个状态。如图24.6所示,这帧在状态S3上的条件概率最大,因此就猜这帧属于状态S3。

图24.6 由音素确定其对应状态的过程
图24.6中的那些用到的概率从哪里读取呢?有个叫“声学模型”的东西,里面存了一大堆参数,通过这些参数,就可以知道帧和状态对应的概率。获取这一大堆参数的方法叫做“训练”,需要使用巨大数量的语音数据,训练的方法比较繁琐,这里不讲。
但这样做有一个问题:每一帧都会得到一个状态号,最后整个语音就会得到一堆乱七八糟的状态号,相邻两帧间的状态号基本都不相同。假设语音有1000帧,每帧对应1个状态,每3个状态组合成一个音素,那么大概会组合成300个音素,但这段语音其实根本没有这么多音素。如果真这么做,得到的状态号可能根本无法组合成音素。实际上,相邻帧的状态应该大多数都是相同的才合理,因为每帧很短。这里就涉及第二步,如何由状态合成音素的问题。
解决这个问题的常用方法就是使用隐马尔可夫模型(Hidden Markov Model,HMM)。这东西听起来好像很高深的样子,实际上用起来很简单,主要由以下两步来实现:
第一步,构建一个状态网络;
第二步,从状态网络中寻找与声音最匹配的路径。
这样就把结果限制在预先设定的网络中,避免了刚才说到的问题。当然也带来一个局限,比如你设定的网络里只包含了“今天晴天”和“今天下雨”两个句子的状态路径,那么不管说些什么,识别出的结果必然是这两个句子中的一句。
如果想识别任意文本呢?只要把这个网络搭得足够大,包含任意文本的路径就可以了。但这个网络越大,想要达到比较好的识别准确率就越难。所以要根据实际任务的需求,合理选择网络大小和结构。
如何由音素合成单词呢?首先搭建状态网络,它是由单词级网络展开成音素网络,再展开成状态网络,其中涉及到HMM、词典以及语言模型等。具体如图24.7所示。语音识别过程其实就是在状态网络中搜索一条最佳路径,语音对应这条路径的概率最大,这称之为“解码”。路径搜索的算法是一种动态规划剪枝的算法,称之为Viterbi算法,用于寻找全局最优路径。

图24.7 状态网络示意图
这里所说的概率,由三部分构成,分别是:
观察概率:每帧和每个状态对应的概率
转移概率:每个状态转移到自身或转移到下个状态的概率
语言概率:根据语言统计规律得到的概率
其中,前两种概率从声学模型中获取,最后一种概率从语言模型中获取。语言模型是使用大量的文本训练出来的,可以利用某门语言本身的统计规律来帮助提升识别正确率。语言模型很重要,如果不使用语言模型,当状态网络较大时,识别出的结果基本是一团乱麻。
至此,语音识别主要过程就完成了,这就是语音识别技术的原理。
【备注】本节内容主要参考知乎,作者:张俊博,链接:https://www.zhihu.com/question/20398418/answer/18080841

24.3 语音识别发展历程

语音识别与上世纪50年代就开始了,当时主要尝试对单个的孤立词进行识别;
60-70年代人们开始开始探索连续语言识别工作,但进展比较缓慢;
80年代得到开始发展,得益于两个关键技术的应用:隐马尔科夫模型(HMM)的理论和N-gram语言模型;
90年代是语音识别基本成熟的时期,主要进展是语音识别声学模型的区分性训练准则和模型自适应方法的提出。这个时期剑桥语音识别组推出的HTK工具包对于促进语音识别的发展起到了很大的推动作用。此后语音识别发展很缓慢,主流的框架GMM-HMM趋于稳定,但是识别效果离实用化还相差甚远,语音识别的研究陷入了瓶颈;
2006年。这一年辛顿(Hinton)提出深度置信网络(DBN),促使了深度神经网络(Deep Neural Network,DNN)研究的复苏,掀起了深度学习的热潮,从此语音识别开始了新篇章,基于GMM-HMM的语音识别框架被打破,大量研究人员开始转向基于DNN-HMM的语音识别系统的研究。用DNN替换GMM,主要优势有:
(1)使用DNN估计HMM的状态的后验概率分布不需要对语音数据分布进行假设;
(2)DNN的输入特征可以是多种特征的融合,包括离散或者连续的;
(3) DNN可以利用相邻的语音帧所包含的结构信息。
图24.8为基于深度神经网络的语音识别系统框架。

图24.8 基于深度神经网络的语音识别系统框架
不过这种框架中DNN对语音信号的长时相关性解决不很理想,于是,循环神经网络(Recurrent Neural Network,RNN)近年来逐渐替代传统的DNN成为主流的语音识别建模方案。如图24.9。


图24.9 基于RNN和CTC的主流语音识别系统框架
循环神经网络在隐层上增加了一个反馈连接,这就很好解决长时相关性问题,再加上序列短时分类(Connectionist Temporal Classification,CTC)输出层,使整个训练更加高效,实现有效的“端对端”训练。
近几年,随着大数据、深度学习及云计算的进一步发展和融合,语音识别性能得到进一步提升,现在已有很多商业化应用,并且正在不断拓展其应用的广度和深度。

第14章TensorFlowOnSpark详解

前面我们介绍了Spark MLlib的多种机器学习算法,如分类、回归、聚类、推荐等,Spark目前还缺乏对神经网络、深度学习的足够支持,但近几年市场对神经网络,尤其对深度学习热情高涨,成了当下很多企业的研究热点,缺失神经网络的支持,这或许也算是Spark MLlib尚欠不足之处吧。
不过好消息是TensorFlow这个深度学习框架,已经有了Spark接口,即TensorFlowOnSpark。TensorFlow是目前很热门的深度学习框架,是Google于2015年11月9日开源的第二代深度学习系统,也是AlphaGo的基础程序。
本章我们将介绍深度学习最好框架TensorFlow及TensorFlowOnSpark,具体包括:
TensorFlow简介
TensorFlow实现卷积神经网络
分布式TensorFlow
TensorFlowOnSpark架构
TensorFlowOnSpark实例

14.1TensorFlow简介

14.1.1TensorFlow的安装

安装TensorFlow,因本环境的python2.7采用anaconda来安装,故这里采用conda管理工具来安装TensorFlow,目前conda缺省安装版本为TensorFlow 1.1。

验证安装是否成功,可以通过导入tensorflow来检验。
启动ipython(或python)

14.1.2TensorFlow的发展

2015年11月9日谷歌开源了人工智能系统TensorFlow,同时成为2015年最受关注的开源项目之一。TensorFlow的开源大大降低了深度学习在各个行业中的应用难度。TensorFlow的近期里程碑事件主要有:
2016年04月:发布了分布式TensorFlow的0.8版本,把DeepMind模型迁移到TensorFlow;
2016年06月:TensorFlow v0.9发布,改进了移动设备的支持;
2016年11月:TensorFlow开源一周年;
2017年2月:TensorFlow v1.0发布,增加了Java、Go的API,以及专用的编译器和调试工具,同时TensorFlow 1.0引入了一个高级API,包含tf.layers,tf.metrics和tf.losses模块。还宣布增了一个新的tf.keras模块,它与另一个流行的高级神经网络库Keras完全兼容。
2017年4月:TensorFlow v1.1发布,为 Windows 添加 Java API 支,添加 tf.spectral 模块, Keras 2 API等;
2017年6月:TensorFlow v1.2发布,包括 API 的重要变化、contrib API的变化和Bug 修复及其他改变等。

14.1.3TensorFlow的特点

14.1.4TensorFlow编程模型

TensorFlow如何工作?我们通过一个简单的实例进行说明,为计算x+y,你需要创建下图(图14-1)这张数据流图:

图14-1计算x+y的数据流图

以下构成上数据流图(图14-1)的详细步骤:
1)定义x= [1,3,5],y =[2,4,7],这个图和tf.Tensor一起工作来代表数据的单位,你需要创建恒定的张量:

2)定义操作

3)张量和操作都有了,接下来就是创建图

注意:这一步不是必须的,在创建回话时,系统将自动创建一个默认图。
4)为了运行这图你将需要创建一个回话(tf.Session),一个tf.Session对象封装了操作对象执行的环境,为了做到这一点,我们需要定义在会话中将要用到哪一张图:

5)想要执行这个操作,要用到tf.Session.run()这个方法:

6)运行结果:
[ 3 7 12]

14.1.5TensorFlow常用函数

14.1.6TensorFlow的运行原理

TensorFlow有一个重要组件client,即客户端,此外,还有master、worker,这些有点类似Spark的结构。它通过Session的接口与master及多个worker相连,其中每一个worker可以与多个硬件设备(device)相连,比如CPU或GPU,并负责管理这些硬件。而master则负责管理所有worker按流程执行计算图。

14.2TensorFlow实现卷积神经网络

神经网络可为机器学习中最活跃的领域之一,尤其代表深度学习的卷积神经(Convolutional Neural Network,CNN)、循环神经网络(Recurrent Neural Network,RNN)更是炙手可热。

14.2.1卷积神经网络简介

卷积神经网络是人工神经网络的一种,已成为图像识别、视频处理、语音分析等领域的研究热点。它的权值共享网络结构使之更类似于生物神经网络,减少了权值的数量,降低了网络模型的复杂度,防止因参数太多导致过拟合。

14.2.3卷积神经网络的网络结构

接下来,我们利用训练集训练卷积神经网络模型,然后在测试集上验证该模型。
搭建的卷积神经网络使用的一些参数是:
卷积层1:kernel_size [5, 5], stride=1,32个卷积窗口
池化层1: pool_size [2, 2], stride = 2
卷积层2:kernel_size [5, 5], stride=1,64个卷积窗口
池化层2: pool_size [2, 2], stride = 2
全连接层: 1024个特征,使用dropout减少过拟合
输出层: 使用softmax进行分类

14.2.4.1 导入数据

首先启动ipython,进入交互计算环境,当然直接启动python也可,然后通过TensorFlow自带的函数读取图片数据。

如果无法直接通过input_data下载,可以先把MNIST数据下载,然后,修改
python/learn/datasets/mnist.py文件中read_data_sets函数中4个local_file的值
具体如下,注释原来的local_file,新增4行local_file

更加数据实际存放路径,修改read_data_sets中读取文件路径。

14.2.4.2 权重初始化

# 正态分布,标准差为0.1,默认最大为1,最小为-1,均值为0

14.2.4.3 构建卷积神经网络结构

14.2.4.4 训练评估模型

这里只迭代了2000次,运行结果;如果迭代20000次,在测试集上的精度可到99.2%左右
## -- End pasted text --
step 0, training accuracy 0.14
step 100, training accuracy 0.86
step 200, training accuracy 0.94
step 300, training accuracy 0.94
step 400, training accuracy 0.94
step 500, training accuracy 0.98
step 600, training accuracy 0.94
step 700, training accuracy 0.94
step 800, training accuracy 0.98
step 900, training accuracy 0.98
step 1000, training accuracy 1
step 1100, training accuracy 0.94
step 1200, training accuracy 0.98
step 1300, training accuracy 0.96
step 1400, training accuracy 0.92
step 1500, training accuracy 0.96
step 1600, training accuracy 0.96
step 1700, training accuracy 1
step 1800, training accuracy 1
step 1900, training accuracy 0.96

在测试集上,测试模型精度

14.3TensorFlow实现循环神经网络

14.3.1循环神经网络简介

在传统的神经网络模型中,是从输入层到隐含层再到输出层,层与层之间是全连接的,
每层之间的节点是无连接的。但是这种普通的神经网络对于很多问题却无能无力。

14.3.2LSTM循环神经网络简介

LSTM是一种特殊的RNNs,可以很好地解决长时依赖问题。

14.3.4TensorFlow实现循环神经网络

前面我们用卷积神经网络,对MNIST中的手写数进行设别,如果迭代20000次,精度可达到99.2左右,这个精度应该比较高;如果我们用循环神经网络来识别,是否可行?如果可以,效果如何?
为了适合使用RNN来识别,每张图片大小为28x28像素,我们把每张图片的每一行(元素个数为28)作为输入数据n_inputs,把每一行(一张图片共28行)看成是与时间序列有关的步数n_steps,这样图片的所有信息都用上了,而且适合使用RNN的应用场景。
启动ipython,进入ipython的交互式界面,导入需要的库,并启动交互式会话。

加载数据,具体实现细节可参考14.2.4.1小节,这里就不详细说明了。

1. 构建模型
设置训练模型的超参数,学习速率,批量大小等。

设置循环神经网络的参数,包括输入数长度,输入的步数,隐藏节点数,类别数等。

定义输入数据及权重等

定义权重及初始化偏移量

定义并初始化Input gate、Forget gate、Output gate、Memory cell等的输入数据、权重、偏移量,这里采用tensorflow中truncated_normal函数初始化相关参数值。

创建循环神经网络结构

2. 定义损失函数及优化器

3. 训练数据及评估模型

运行结果,以下是最后批次的运行结果。
Iter 8000, Minibatch Loss= 0.085752, Training Accuracy= 0.97656
Iter 8100, Minibatch Loss= 0.065435, Training Accuracy= 0.96875
Iter 8200, Minibatch Loss= 0.088926, Training Accuracy= 0.97656
Iter 8300, Minibatch Loss= 0.039572, Training Accuracy= 1.00000
Iter 8400, Minibatch Loss= 0.050593, Training Accuracy= 0.98438
Iter 8500, Minibatch Loss= 0.030424, Training Accuracy= 0.99219
Iter 8600, Minibatch Loss= 0.026174, Training Accuracy= 0.99219
Iter 8700, Minibatch Loss= 0.045043, Training Accuracy= 0.98438
Iter 8800, Minibatch Loss= 0.031143, Training Accuracy= 0.98438
Iter 8900, Minibatch Loss= 0.055115, Training Accuracy= 0.99219
Iter 9000, Minibatch Loss= 0.061676, Training Accuracy= 0.98438
Iter 9100, Minibatch Loss= 0.123581, Training Accuracy= 0.97656
Iter 9200, Minibatch Loss= 0.057620, Training Accuracy= 0.98438
Iter 9300, Minibatch Loss= 0.043013, Training Accuracy= 0.99219
Iter 9400, Minibatch Loss= 0.067405, Training Accuracy= 0.98438
Iter 9500, Minibatch Loss= 0.020679, Training Accuracy= 1.00000
Iter 9600, Minibatch Loss= 0.079038, Training Accuracy= 0.98438
Iter 9700, Minibatch Loss= 0.080076, Training Accuracy= 0.97656
Iter 9800, Minibatch Loss= 0.010582, Training Accuracy= 1.00000
Iter 9900, Minibatch Loss= 0.019426, Training Accuracy= 1.00000
Optimization Finished!

在测试集上验证模型

运行结果如下,这个结果虽然比CNN结果低些,但也是不错的一个结果。
Testing Accuracy: 0.976562

14.4分布式TensorFlow

2016年4月14日,Google发布了分布式TensorFlow,能够支持在几百台机器上并行训练。分布式的TensorFlow由高性能的gRPC库作为底层技术支持。

14.4.1客户端、主节点和工作节点间的关系

14.4.2分布式模式

常用的深度学习训练模型为数据并行化,即TensorFlow任务采用相同的训练模型在不同的小批量数据集上进行训练,然后在参数服务器上更新模型的共享参数。TensorFlow支持同步训练和异步训练两种模型训练方式。

14.4.3在Pyspark集群环境运行TensorFlow

这节将通过神经网络来模拟一个一元二次方程:y=x^2-0.5,
TensorFlowOnSpark的详细配置,请参考14.4节。已集群方式启动pyspark:

进入pyspark的交换界面

构造一个神经网络

输出结果:
1.62758
0.00996406
0.00634915
0.00483868
0.0043179
0.00399014
0.00368176
0.00337165
0.00309145
0.00284696
0.00267657
0.00255845
0.0024702
0.00240239
0.00235583
0.00232014
0.00229183
0.00226797
0.00224843

14.5TensorFlowOnSpark架构

TensorFlowOnSpark(TFoS),支持 TensorFlow 在 Spark 和 Hadoop 上的分布式运行。

14.6TensorFlowOnSpark安装

安装TensorFlowOnSpark,采用pip管理工具进行安装,缺省安装是1.0版本。

执行以上命令后,在用户当前目录下,将新增一个TensorFlowOnSpark目录。
然后,在.bashrc定义该路径。

可以通过pyspark环境来验证,以上2个安装是否成功。

导入这些库,如果没有异常,说明安装成功。接下来开始为训练数据做一些准备工作。
对scripts目录进行打包,便于把该包发布到各worker上

14.7TensorFlowOnSpark实例

使用TensorFlowOnSpark对MNIST数据进行预测,MNIST是一个手写数字数据库,它有60000个训练样本集和10000个测试样本集,train-images-idx3-ubyte.gz、train-labels-idx1-ubyte.gz等四个文件。这些图像数据都保存在二进制文件中。每个样本图像的宽高为28*28。
下载MNIST数据

14.7.1TensorFlowOnSpark单机模式实例

设置本机相关参数,在单机上启动一个master节点,两个worker节点。

启动以后,通过jps可以看到如下一些进程。一个master,两个worker,namenode是之前启动hadoop的进程。

相关服务起来后,接下来把MNIST数据上传到HDFS上,并把数据转换cvs格式。

运行完成后,通过hadoop fs命令可以在HDFS上看到如下信息:

数据加载转换成功后,开始训练数据。

运行完成后,可以看到如下内容:

如果运行过程中,过程被卡,可以调整mnist_dist.py文件中两处(在115,125行)logdir=logdir改为logdir=None。
训练完成后,接下来就是用测试集验证模型,并对结果进行预测。

运行完成以后,在HDFS上,就可看到predictions目录及相关内容。

打开其中一个文件,可以看到预测结果信息。
2017-06-18T05:51:42.397905 Label: 5, Prediction: 5
2017-06-18T05:51:42.397923 Label: 9, Prediction: 8
2017-06-18T05:51:42.397941 Label: 7, Prediction: 5
2017-06-18T05:51:42.397958 Label: 3, Prediction: 5
2017-06-18T05:51:42.397976 Label: 4, Prediction: 8
2017-06-18T05:51:42.397993 Label: 9, Prediction: 8
2017-06-18T05:51:42.398012 Label: 6, Prediction: 5

14.7.2TensorFlowOnSpark集群模式实例

设置本机相关参数,在以集群方式启动spark,一个master节点,slave1、slave2作为
两个worker节点,各节点资源配置信息。
训练模型

测试模型

查看运行结果

运行时各节点报错信息,可以查看spark/work/app-20170620085449-0003/1下的

14.8小结

为了弥补Spark机器学习中,缺乏神经网络、深度学习等的不足,这章我们介绍脱胎于AlphaGo的深度学习框架TensorFlow,以基础知识为主,在这个基础上介绍了使用TensorFlow的几个实例,最后介绍TensorFlow的分布式架构及与Spark整合的架构TensorFlowOnSpark。

第13章 使用Spark Streaming构建在线学习模型

前面我们介绍的这些算法,一般基于一个或几个相对固定的文件,以这样的数据为模型处理的源数据是固定的,这样的数据或许很大,很多。训练或测试都是建立在这些固定数据之上,当然,测试时,可能取这个数据源之外的数据,如新数据或其他数据等。训练模型的数据一般是相对固定的,这样的机器学习的场景是很普遍的。
但实际环境中,还有其他一些场景,如源数据是经常变换,就像流水一样,时刻在变换着,如很多在线数据、很多日志数据等等。面对这些数据的学习我们该如何处理呢?
这个问题实际上属于流水计算问题,目前解决这类问题有Spark Streaming、Storm、Samza等。这章我们主要介绍Spark Streaming。
本章主要包括以下内容:
 介绍Spark Streaming主要内容
 Spark Streaming入门实例
 在线学习实例

13.1 Spark Streaming简介

Spark Streaming 是Spark核心API的一个扩展,可以实现高吞吐量的、具备容错机制的实时流数据的处理。支持从多种数据源获取数据,包括Kafk、Flume、Twitter、ZeroMQ、Kinesis 以及TCP sockets,从数据源获取数据之后,可以使用诸如map、reduce、join和window等高级函数进行复杂算法的处理。最后还可以将处理结果存储到文件系统,数据库和现场仪表盘。在“One Stack rule them all”的基础上,还可以使用Spark的其他子框架,如集群学习、图计算等,对流数据进行处理。

13.1.1Spark Streaming常用术语

在简介Spark Streaming前,我们先简单介绍Streaming的一些常用术语。

13.1.2Spark Streaming处理流程

Spark Streaming处理的数据流图如图13-1所示。

图13-1 Spark Streaming计算过程

13.2 Dstream操作

RDD有很多操作和转换,与RDD类似,DStream也提供了自己的一系列操作方法,本节主要介绍如何操作DStream,包括输入、转换、修改状态及输出等。

13.2.1 Dstream输入

在Spark Streaming中所有的操作都是基于流的,而输入源是一切操作的起点。
Spark Streaming 提供两种类型的流式输入数据源:
 基础输入源:能直接应用于StreamingContext API输入源。例如:文件系统、Socket(套接字)连接和 Akka actors;
 高级输入源:能应用于特定工具类的输入源,如 Kafka、Flume、Kinesis、Twitter 等,使用这些输入源需要导入一些额外依赖包。

13.2.2 Dstream转换

DStream转换操作是在一个或多个DStream上创建新的DStream。

13.2.3 Dstream修改

Spark Streaming除提供一些基本操作,还提供一些状态操作。

13.2 .4Dstream输出

Spark Streaming允许DStream的数据输出到外部系统,如数据库、文件系统等。

13.3 Spark Streaming应用实例

先启动nc,端口为9999

然后,以本地方式启动spark shell

在启动了nc的界面输入:

在spark shell界面,可以看到如下输出:

13.4 Spark Streaming在线学习实例

前面我们简单介绍一个利用nc产生文本数据,Spark Streaming实时统计词频的一个实例,通过这个例子,我们对Streaming有个大致了解,它的源数据可以是实时产生、实时变化的,基于这个数据流,Spark Streaming能实时进行统计词频信息,并输出到界面。
除了统计词频,实际上Spark Streaming 还可以做在线机器学习工作,目前Spark Streaming支持Streaming Linear Regression, Streaming KMeans等,这节我们模拟一个在线学习线性回归的算法,源数据为多个文件,首先在一个文件中训练模型,然后在新数据上进行调整模型,对新数据进行预测等。

13.5小结

前几章主要介绍了Spark ML对批量数据或离线数据的分析和处理,本章主要介绍Spark Streamin对在线数据或流式数据的处理及分析,首先对Spark Streaming的一些概念、输入源、Dstream的一些转换、修改、输出作了简单介绍,然后,通过两个实例把这些内容结合在一起,进一步说明Spark Streaming在线统计、在线学习的具体使用。


本章数据集下载
第12章 Spark R 朴素贝叶斯模型

前一章我们介绍了PySpark,就是用Python语言操作Spark大数据计算框架上的任务,这样把自然把Python的优点与Spark的优势进行叠加。Spark提供了Python的API,也提供了R语言的API,其组件名称为Spark R。Spark R的运行原理或架构,具体请看图12-1。

 

 

图12-1 Spark R 架构图

Spark R的架构类似于PySpark,Driver端除了一个JVM进程(包含一个SparkContext,在Spark2.X中SparkContext已经被SparkSession所代替)外,还有起一个R的进程,这两个进程通过Socket进行通信,用户可以提交R语言代码,R的进程会执行这些R代码,
当R代码调用Spark相关函数时,R进程会通过Socket触发JVM中的对应任务。
当R进程向JVM进程提交任务的时候,R会把子任务需要的环境进行打包,并发送到JVM的driver端。通过R生成的RDD都会是RRDD类型,当触发RRDD的action时,Spark的执行器会开启一个R进程,执行器和R进程通过Socket进行通信。执行器会把任务和所需的环境发送给R进程,R进程会加载对应的package,执行任务,并返回结果。
本章通过一个实例来说明如何使用Spark R,具体内容如下:
 Spark R简介
 把数据上传到HDFS,然后导入Hive,最后从Hive读取数据
 使用朴素贝叶斯分类器
 探索数据
 预处理数据
 训练模型
 评估模型

12.1. Spark R简介

目前SparkR的最新版本为2.0.1,API参考文档(http://spark.apache.org/docs/latest/api/R/index.html)。

12.2获取数据

12.2.1 SparkDataFrame数据结构说明

SparkDataFrame是Spark提供的分布式数据格式(DataFrame)。类似于关系数据库中的表或R语言中的DataFrame。SparkDataFrames可以从各种各样的源构造,例如:结构化数据文件,Hive中的表,外部数据库或现有的本地数据。

12.2.2创建SparkDataFrame

1.从本地文件加载数据,生成SparkDataFrame
SparkR支持通过SparkDataFrame接口对各种数据源进行操作。示例:

2.利用R环境的data frames创建SparkDataFrame
创建SparkDataFrame的最简单的方法是将本地R环境变量中的data frames转换为SparkDataFrame。我们可以使用as.DataFrame或createDataFrame函数来创建SparkDataFrame。作为示例,我们使用R自带的iris数据集来创建SparkDataFrame。

3.从HDFS文件系统加载数据,生成SparkDataFrame
示例:

4.读取Hive数据仓库中的表,生成SparkDataFrame
我们还可以从Hive表创建SparkDataFrame。

12.2.3 SparkDataFrame的常用操作

1.选择行,或者列

2.数据分组,聚合

3.对SparkDataFrame的列进行运算操作

4.apply系列函数的应用
• dapply函数类似于R语言的apply函数,看一个示例。

12.3朴素贝叶斯分类器

该案例数据来自泰坦尼克号人员存活情况,响应变量为Survived,包含2个分类(yes,no),特征变量有Sex 、 Age 、Class(船舱等级),说明如下:
Class :0 = crew, 1 = first, 2 = second, 3 = third Age :1 = adult, 0 = child Sex :1 = male, 0 = female Survived :1 = yes, 0 = no

12.3.1数据探查

让我们来观察一下数据,

12.3.2对原始数据集进行转换

12.3.3查看不同船舱的生还率差异

然后,对比一下不同性别之间的生还率:

最后再看看不同年龄段的生还情况:

12.3.4转换成SparkDataFrame格式的数据

12.3.5模型概要

12.3.6预测

12.3.7评估模型

计算准确率

计算召回率

计算精准率

12.4 小结

本章主要介绍了如何使用Spark R组件的问题,Spark R 给R开发人员提供很多API,利用这些API,开发人员就可以通过R语言操作Spark,把用R编写的代码放在Spark这个大数据技术平台运行,这样可以使R不但可以操作HDFS或Hive中数据,也自然使用Spark分布式基于内存的架构。