天天看点

Python 数据分析实例——神经网络—深度学习

作者:昌华量化
Python 数据分析实例——神经网络—深度学习

深度学习(Deep Learning)是机器学习的一个分支,可以理解为具有多层结构的模型。具体来说,深度学习是机器学习中的具有深层结构的神经网络算法,即机器学习>神经网络>深度神经网络(深度学习)。

神经网络技术起源于20世纪五六十年代,当时叫感知器(Perceptron),拥有输入层、输出层和一个隐含层。输入的特征向量通过隐含层变换达到输出层,在输出层得到分类结果。多层感知器可以摆脱早期离散传输函数的束缚,使用Sigmoid或tanh等连续函数模拟神经元对激励的响应,在训练算法上则使用Werbos发明的反向传播BP算法。随着神经网络层数的加深,有两个重大问题:一是优化函数越来越容易陷入局部最优解,并且这个“陷阱”越来越偏离真正的全局最优,利用有限数据训练的深层网络,性能还不如较浅层网络;二是“梯度消失”现象更加严重。2006年,Hinton利用预训练方法缓解了局部最优解问题,将隐含层推动到了7层,神经网络真正意义上有了“深度”,由此揭开了深度学习的热潮,随后的DBN、CNN、RNN、LSTM等才逐渐出现。这里的“深度”并没有固定的定义—在语音识别中4层网络就能够被认为是“较深的”,而在图像识别中20层以上的网络屡见不鲜。

随着神经网络层数的加深,有三个重大问题:一是非凸优化问题,即优化函数越来越容易陷入局部最优解;二是梯度消失(Gradient Vanish)问题;三是过拟合问题。

一、深度学习的基本模型

深度学习里面的基本模型大致分为3类:多层感知器模型、深度神经网络模型和递归神经网络模型。其代表分别是深度信念网络(Deep Belief Network,DBN)、卷积神经网络、递归神经网络。

1.深度信念网络

2006年,Geoffrey Hinton提出深度信念网络及其高效的学习算法,即Pre-Training+Fine Tuning,并发表于《Science》上,成为其后深度学习算法的主要框架。深度信念网络是一种生成模型,通过训练其神经元间的权重,我们可以让整个神经网络按照最大概率来生成训练数据。所以,我们不仅可以使用深度信念网络识别特征、分类数据,还可以用它来生成数据。

(1)深度信念网络结构

深度信念网络由若干层受限玻尔兹曼机(Restricted Boltzmann Machine,RBM)堆叠而成,上一层受限玻尔兹曼机的隐层作为下一层受限玻尔兹曼机的可见层,如图1所示。

Python 数据分析实例——神经网络—深度学习

图1 深度信念网络结构

深度信念网络模型由若干层受限玻尔兹曼机堆叠而成,如果在训练集中有标签数据,那么最后一层受限玻尔兹曼机的可见层中既包含前一层受限玻尔兹曼机的隐层单元,又包含标签层单元。假设顶层受限玻尔兹曼机的可见层有500个神经元,训练数据的分类一共分成了10类,那么顶层受限玻尔兹曼机的可见层有510个显性神经元,对每一训练数据,相应的标签神经元被打开设为1,而其他的则被关闭设为0。

(2)训练过程

深度信念网络的训练包括Pre-Training和Fine Tuning两步,其中Pre-Training过程相当于逐层训练每一个受限玻尔兹曼机,经过Pre-Training的深度信念网络已经可用于模拟训练数据,而为了进一步提高网络的判别性能,Fine Tuning过程利用标签数据通过BP算法对网络参数进行微调。

(3)改进模型

深度信念网络的变体比较多,它的改进主要集中于其组成“零件”受限玻尔兹曼机的改进,有卷积深度信念网络(CDBN)和条件受限玻尔兹曼机(Conditional RBM)等。深度信念网络并没有考虑到图像的二维结构信息,因为输入只是简单地将一个图像矩阵转换为一维向量。而CDBN利用邻域像素的空域关系,通过一个称为卷积受限玻尔兹曼机(CRBM)的模型达到生成模型的变换不变性,而且容易变换到高维图像。深度信念网络并没有明确地处理对观察变量的时间联系的学习,条件受限玻尔兹曼机通过考虑前一时刻的可见层单元变量作为附加的条件输入,以模拟序列数据,这种变体在语音信号处理领域应用较多。

2.卷积神经网络

卷积神经网络是人工神经网络的一种,已成为当前语音分析和图像识别领域的研究热点。它的权值共享网络结构使之更类似于生物神经网络,降低了网络模型的复杂度,减少了权值的数量。该优点在网络的输入是多维图像时表现得更为明显,使图像可以直接作为网络的输入,避免了传统识别算法中复杂的特征提取和数据重建过程。卷积网络是为识别二维形状而特殊设计的一个多层感知器,这种网络结构对平移、比例缩放、倾斜或者共他形式的变形具有高度不变性。

(1)网络结构

卷积神经网络是一个多层的神经网络,其基本运算单元包括:卷积运算、池化运算、全连接运算和识别运算,如图2所示。

Python 数据分析实例——神经网络—深度学习

图2 卷积神经网络结构

卷积神经网络与常规的神经网络十分相似,它们都由可以对权重和偏置进行学习的神经元构成。每个神经元接收一些输入,然后执行点积操作,再紧接一个可选的非线性函数。整个网络仍然表示为单可微分的评估函数,整个网络从一端输入原始图像像素,另一端输出类别的概率。其最后一层(全连接层)同样有损失函数,并且学习常规神经网络的方法和技巧在这里仍然奏效。卷积网络很明确地假设所有输入都为图像,这就允许在结构中对明确的属性进行编码。这使得前向函数的实现更加高效,并且极大地减少了网络中参数的数量。

一个简单的卷积网络由一系列的层构成,并且卷积网络的每一层通过一个可微函数将一个激活量转换成另一个激活量。用3类主要的层来构造卷积网络:卷积层、池化层以及全连接层。我们将通过堆叠这些层来形成一个完整的卷积网络结构。

三维神经元:卷积神经网络充分利用了输入是由图像组成的这一事实,并以更加合理的方式对结构进行了约束。特别是,与常规网络不同,卷积网络各层的神经元具有3个维度,即宽度、高度、深度。每一层的神经元仅与前一层的局部相连,而不是采用全连接的方式。

(2)卷积层

卷积层是一个卷积神经网络的核心组成部分,它负责完成大部分计算繁重的工作。首先,卷积层的参数由一组可以进行学习的滤波器组成。每一个滤波器都是一个小的二维面区域(沿宽度和高度),如图3所示,但却延伸到输入激活量的全部深度。在正向传播期间,沿着输入方柱体的宽度和高度方向滑动(更确切地说是卷积)每个滤波器,并计算滤波器的端口与滤波器处于每个位置上的输入的点积。当将滤波器沿着输入激活量的高度和宽度滑过之后,将会得到一个二维的激活图,该图给出了滤波器在滑动过的每个位置上得到的结果。直观地说,网络将对滤波器进行学习,当看到某种类型的视觉特征时激活,例如第一层上某种方向的条棱或某种颜色的斑点,或者最终在网络的更高层上形成的整个蜂窝或轮状图案。接下来,将在每个卷积层上使用一整组的滤波器,其中每一个滤波器都将产生一个二维激活图。将这些激活图沿着深度堆叠起来,得到一个输出激活量。

Python 数据分析实例——神经网络—深度学习

图3 典型滤波器(3)训练过程

卷积网络在本质上是一种输入到输出的映射,它能够学习大量的输入与输出之间的映射关系,而不需要任何输入和输出之间的精确的数学表达式,只要用已知的模式对卷积网络加以训练,网络就具有输入输出对之间的映射能力。卷积网络执行的是有监督训练,所以其样本集是由形如(输入信号,标签值)的向量对构成的。

3.递归神经网络

全连接的深度神经网络存在一个问题—无法对时间序列上的变化进行建模。然而,样本出现的时间顺序对于自然语言处理、语音识别、手写体识别等应用非常重要。对了适应这种需求,就出现了递归神经网络。在普通的全连接网络或卷积神经网络中,每层神经元的信号只能向上一层传播,样本的处理在各个时刻独立,因此又被称为前向神经网络(Feed-Forward Neural Networks)。而在递归神经网络中,神经元的输出可以在下一个时间戳直接作用到自身。递归神经网络可以看成一个在时间上传递的神经网络,它的深度是时间的长度。正如前面介绍的,“梯度消失”现象又要出现了,只不过这次发生在时间轴上。为了解决时间上的梯度消失,机器学习领域发展出了长短时记忆单元(LSTM),通过门的开关实现时间上的记忆功能,并防止梯度消失。

(1)训练过程

递归神经网络中由于输入时叠加了之前的信号,因此反向传导时不同于传统的神经网络,因为对于时刻t的输入层,其残差不仅来自于输出,还来自于之后的隐层。通过反向传递算法,利用输出层的误差求解各个权重的梯度,然后利用梯度下降法更新各个权重。

(2)改进模型

递归神经网络模型可以用来处理序列数据,递归神经网络包含大量参数,且难于训练(时间维度的梯度消散或梯度爆炸),所以出现了一系列对递归神经网络的优化,比如网络结构、求解算法与并行化。近年来,双向循环神经网络(BRNN)与长短时记忆单元在image captioning(图题)、language translation(翻译)、handwriting recognition(手写识别)这几个方向上有了突破性进展。

二、新闻分类实例

【例1】新闻分类(多分类问题)。

在本例题中,代码示例使用Keras(https://keras.io)。Keras是Python的一个深度学习框架,它提供了一种方便的方式来定义和训练几乎任何类型的深度学习模型。Keras最初是为研究人员开发的,目的是实现快速实验。Keras是以MIT License发布的,这意味着它可以在商业项目中自由使用。它兼容任何版本的Python,从2.7到3.6(2017年中期)。

Keras是一个模型级库,提供了开发深度学习模型的高级构建模块。它不处理低级操作,如张量操作和微分。相反,它依赖于一个专门的、经过良好优化的张量库,充当Keras的后端引擎。它不是选择单个张量库并将Keras的实现与该库绑定在一起,而是以模块化的方式处理问题,因此可以将几个不同的后端引擎无缝地插入Keras中。目前,现有的3个后端实现是TensorFlow后端、Theano后端和Microsoft Cognitive工具包(CNTK)的后端。TensorFlow、CNTK和Theano是当今深度学习的主要平台。Theano(http://deeplearning.net/software/theano)由蒙特利尔大学的MILA实验室开发,TensorFlow(www.tensorflow.org)由谷歌开发,CNTK(https://github.com/Microsoft/CNTK)由微软开发。用户使用Keras编写的任何代码片段都可以与这些后端一起运行,而无须更改代码中的任何内容。

本节示例构建一个网络,将路透社新闻专线分为46个相互排斥的主题。因为有很多类,所以这个问题是一个多分类的实例;而且,由于每个数据点都应该被划分为一个类别,因此这个问题更具体地说就是一个单标签、多分类(Single-Label,Multiclass Classification)的实例。如果每个数据点可能属于多个类别(在本例中是主题),那么将面临一个多标签、多分类(Multi-Label,Multiclass Classification)的问题。

本例题实验环境为Anaconda 3 + Jupyter NoteBook。

1.加载数据集

In[1]:from keras.datasets import reuters
(train_data, train_labels), (test_data, test_labels) = reuters.load_data
(num_words=10000)
#将数据限定在10000个常见的单词、8982个训练样本和2264个测试样本
In[2]:len(train_data)
Out[2]:8982
In[3]: len(test_data)
Out[3]: 2246
#每个示例都是一个整数列表(单词索引)
In[4]: train_data[10]
Out[4]:       [1,       245,       273,       207,156,  53,74,160,  26,14,46,
296,26,39,74,2979,3554,14,46,4689,4329,86,61,3499,
4795,14,61,451,4329, 17, 12]           

2.将索引解码为新闻文本

In[5]: word_index = reuters.get_word_index()
reverse_word_index = dict([(value, key) for (key, value) in word_index.items()])
# Note that our indices were offset by 3
# because 0, 1 and 2 are reserved indices for "padding", "start of sequence",
and"unknown".
decoded_newswire = ' '.join([reverse_word_index.get(i - 3, '?') for i in
train_data[0]])
In[6]: train_labels[10]
Out[6]:3           

3.编码数据

Python 数据分析实例——神经网络—深度学习

4.模型定义​

In[9]: from keras import models
from keras import layers
model = models.Sequential()
model.add(layers.Dense(64, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(46, activation='softmax'))           

5.编译模型​

#对于这个例子,最好的损失函数是 categorical_crossentropy(分类交叉熵),用于衡量两个概
率分布之间的距离
In[10]:model.compile(optimizer='rmsprop',     loss='categorical_crossentropy',
metrics=['accuracy'])           

6.留出验证集​

#留出1000个样本作为验证集
In[11]:x_val = x_train[:1000]
partial_x_train = x_train[1000:]

y_val = one_hot_train_labels[:1000]
partial_y_train = one_hot_train_labels[1000:]           

7.训练模型​

In[12]: history = model.fit(partial_x_train, partial_y_train, epochs=20,
batch_size = 512, validation_data = (x_val, y_val))           

8.绘制训练损失和验证损失​

In[13]: import matplotlib.pyplot as plt
loss = history.history['loss']
val_loss = history.history['val_loss']

epochs = range(1, len(loss) + 1)
plt.plot(epochs, loss, 'bo', label = 'Training loss')
plt.plot(epochs, val_loss, 'b', label = 'Validation loss')
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show()           

训练损失和验证损失如图4所示。

Python 数据分析实例——神经网络—深度学习

图4 训练损失和验证损失​

9.绘制训练精度和验证精度​

In[14]:plt.clf()     # 清除图像
acc = history.history['acc']
val_acc = history.history['val_acc']
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend()
plt.show()           

训练精度和验证精度如图5所示。

Python 数据分析实例——神经网络—深度学习

图5 训练精度和验证精度​

10.从头开始重新训练一个模型​

#中间层有64个隐藏神经元
In[15]:# 从头开始训练一个新的模型
model = models.Sequential()
model.add(layers.Dense(64, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(46, activation='softmax'))

model.compile(optimizer='rmsprop', loss='categorical_crossentropy',
metrics=['accuracy'])
model.fit(partial_x_train,  partial_y_train,  epochs=9,  batch_size  =  512,
validation_data = (x_val, y_val))
results = model.evaluate(x_test, one_hot_test_labels)
In[16]: results
Out[6]: [1.0231964622134093, 0.7756010686194165]
#这种方法可以得到77%的精度
In[17]: import copy

test_labels_copy = copy.copy(test_labels)
np.random.shuffle(test_labels_copy)
float(np.sum(np.array(test_labels) == np.array(test_labels_copy))) /
len(test_labels)
Out[17]:0.182546749777382
#完全随机的精度约为18%
In[18]: # 在新数据上生成预测结果
predictions = model.predict(x_test)
predictions[0].shape
Out[18]: (46,)
In[19]:np.sum(predictions[0])
Out[19]: 0.99999994
In[20]:np.argmax(predictions[0])
Out[20]:3           

11.处理标签和损失的另一种方法​

In[21]:y_train = np.array(train_labels)
y_test = np.array(test_labels)
model.compile(optimizer='rmsprop', loss='sparse_categorical_crossentropy',
metrics=['acc'])           

12.中间层维度足够大的重要性​

#最终输出是46维的,本代码中间层只有4个隐藏单元,中间层的维度远远小于46
In[22]:model = models.Sequential()
model.add(layers.Dense(64, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(4, activation='relu'))
model.add(layers.Dense(46, activation='softmax'))
model.compile(optimizer='rmsprop', loss='categorical_crossentropy',
metrics=['accuracy'])
model.fit(partial_x_train, partial_y_train, epochs=20, batch_size = 128,
validation_data = (x_val, y_val))
Out[22]: Epoch 20/20
7982/7982 [==============================] - 2s 251us/step - loss: 0.3668 - acc:
0.8959 - val_loss: 1.7518 - val_acc: 0.7120
#验证精度最大约为71%,比前面下降了8%。导致这一下降的主要原因在于,试图将大量信息(这些信息
足够回复46个类别的分割超平面)压缩到维度很小的中间空间           

13.中间层128个​

In[23]:model = models.Sequential()
model.add(layers.Dense(64, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(128, activation='relu'))
model.add(layers.Dense(46, activation='softmax'))

model.compile(optimizer='rmsprop', loss='categorical_crossentropy',
metrics=['accuracy'])
model.fit(partial_x_train, partial_y_train, epochs=9, batch_size = 128,
validation_data = (x_val, y_val))
results = model.evaluate(x_test, one_hot_test_labels)
results

Epoch 9/9
7982/7982 [==============================] - 2s 237us/step - loss: 0.1515 - acc:
0.9551 - val_loss: 0.9722 - val_acc: 0.8220
2246/2246 [==============================] - 0s 160us/step
Out[23]:[1.1663296235425071, 0.7938557435440784]
#精度大约在79%
#尝试了中间层128个,但是迭代20轮,准确率却只有77%,说明迭代次数过高,出现了过拟合           

在单标签、多类分类问题中,网络应该以Softmax激活函数结束,这样它就会输出一个在N输出类上的概率分布。如果需要将数据分类为大量的类别,就应该避免由于中间层太小而在网络中造成信息瓶颈。

继续阅读