深度学习入门

人工智能发展

  • 符号主义人工智能(解决定义明确的逻辑问题)
  • 机器学习
  • 深度学习

机器学习

在预先定义好的假设空间中,利用反馈信号的指引来寻找输入数据的有用表示。

  1. 概率建模

    概率建模最有名的一个算法是朴素贝叶斯算法,这是一类基于贝叶斯定理的机器学习分类器,它假设输入数据的特征都是独立的,这是一个很强的假设,或者说是”朴素”的。另一个密切相关的模型logistic回归,它是一种分类算法,而不是回归算法,它的特点就是简单而通用,适合对数据的初步探索。

  2. 早期的神经网络

    早期,很多人独立地重新发现了反向传播算法,一种利用梯度下降优化来训练一系列参数化运算链的方法,并开始将其应用于神经网络。

  3. 核方法

    核方法是一组分类算法,其中最有名的就是支持向量机(SVM)。SVM的目标是通过在属于两个不同类别的两组数据点之间找到良好决策边界(分割超平面)来解决分类问题。它首先将数据映射到高维表示从而使分类问题简化,然后利用核函数将原始空间中的任意两点映射为这两点在目标表示空间中的距离,尽量让超平面与每个类别最近的数据点之间的距离最大化,从而计算出良好决策边界。核函数通常是人为选择的,而不是从数据中学到的,对于SVM来说,只有分割超平面是通过学习得到的。

  4. 决策树、随机森林、梯度提升机

    决策树是类似于流程图的结构,它能从数据中学习得到,可以对输入数据点进行分类或根据给定的输入来预测输出值。随机森林引入了一种健壮且实用的决策树学习方法,即构建许多决策树,然后将它们的输出集成在一起。与随机森林类似,梯度提升机也是将弱预测模型(通常是决策树)集成的机器学习技术,它使用了梯度提升方法,通过迭代地训练新模型来专门解决之前模型的缺点,从而改进任何机器学习模型的效果,而且效果往往比随机森林要好。

深度学习

学习数据表示的多级方法

这个网络将数字图像转换成与原始图像差别越来越大的表示,而其中关于最终结果的信息却越来越丰富。

数字图像分类模型学到的深度学习

神经网络每层对输入数据所作的具体操作保存在该层的权重中,换句话说,每层实现的变换由其权重来参数化。神经网络损失函数的任务(也叫目标函数)是,衡量神经网络的输出与预期值之间的距离。深度学习的基本技巧是利用这个距离值来对权重值进行微调,这种调节由优化器来完成,它实现了反向传播算法,这也是深度学习的核心算法。

神经网络学习流程

神经网络看似是一个简单的机制,一旦具有足够大的规模,将会产生魔法般的效果。

入门实战

选用mnist手写数字识别数据集,以tensorflow为后端,使用keras深度学习框架,来进行深度学习第一个实战例子

1
2
3
4
5
from keras.datasets import mnist
# 加载mnist数据集
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
print("train_images:", train_images.shape, "\ttrain_labels:", train_labels.shape)
print("test_images:", test_images.shape, "\ttest_labels:", test_labels.shape)

数据集大小为,样本集第一个维度是样本个数,第二、三个维度分别是每张图片的行数和列数。

train_images: (60000, 28, 28) train_labels: (60000,)
test_images: (10000, 28, 28) test_labels: (10000,)

为了更好地理解这个数据集,可视化手写数字图片

1
2
3
4
5
6
7
8
9
10
import matplotlib.pyplot as plt
%matplotlib inline
# 展示数据集
fig = plt.gcf()
fig.set_size_inches(12, 6)
for i in range(10):
ax = plt.subplot(2, 5, i+1)
ax.imshow(train_images[i])
ax.set_title(train_labels[i], fontsize=20)
fig.savefig("mnistshow.jpg", dpi=800)

每张手写数字图片上面就是其标签

mnistshow

然后搭建模型,这里搭建两个全连接层,分别有512个和10个神经元,即第一层输出512个特征,第二层输出10个特征,并设置每层的激活函数。然后编译网络,即指定损失函数、优化器以及监控的指标。

1
2
3
4
5
6
7
8
9
from keras import models
from keras import layers
# 搭建网络
network = models.Sequential()
network.add(layers.Dense(512, activation='relu', input_shape=(28*28, ))) # 第一层输入为一阶张量
network.add(layers.Dense(10, activation='softmax')) # 将返回一个由10个概率值(总和为1)组成的数组
# 编译网络:优化器采用梯度下降的变体rmsprop, 损失函数采用分类交叉熵,
network.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])
network.summary()

值得一提的是模型的第一层需要指定input_shape,即输入数据特征的形状,这个例子就是指每张图片的特征(行列像素值),为了简化,我们将图片打平,使得input_shape由二阶张量(28, 28)变成一阶张量(28*28, )。

接下来将样本集中每张图片”打平”,并对像素值归一化

1
2
3
4
5
# 准备图像数据:(60000, 28, 28)->(60000, 28*28), [0, 255]->[0, 1]
print(train_images[0, 8]) # 第一张图片的第8行数值
train_images = train_images.reshape((60000, 28*28)).astype('float') / 255
test_images = test_images.reshape((10000, 28*28)).astype('float') / 255
print(train_images[0, 28*7:28*8]) #第一张图片的第8行数值, 与归一化之前对比

图像数据处理完后,将标签数据通过to_categorical进行独热分类编码

1
2
3
4
5
# 准备标签
from keras.utils import to_categorical
train_labels = to_categorical(train_labels)
test_labesls = to_categorical(test_labels)
print(train_labels[0]) # 第一个标签编码后的数值

一切准备就绪,调用fit()函数开始训练。使用GPU加速,顺利的话应该10s左右就能完成训练。

1
history = network.fit(train_images, train_labels, epochs=5, batch_size=128, validation_split=0.2, verbose=2)

训练过程

train

可以看出来,不管是训练集还是验证集,loss整体是下降趋势,accuracy整体是上升趋势,并且在训练集上达到了98.8%的精度。现在我们调用evaluate()函数来检查模型在测试集上的性能。

1
2
3
test_loss, test_acc = network.evaluate(test_images, test_labels)
print("test_loss:", test_loss)
print("test_acc:", test_acc)

评估结果如下

test_loss: 0.07190389850810171
test_acc: 0.9781000018119812

可以看出来,经过5轮训练的模型在测试集上的精度达到了97.8%,比训练集要低一些。我们观察下面训练集和验证集的loss下降曲线,也能看出来验证集的效果要略差于训练集,这就是过拟合现象,之后再说。

1
2
3
4
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.legend(['train', 'validation'])
plt.savefig("loss.jpg", dpi=800)

loss

小结

可以看出来通过keras搭建模型,我们只需要关注模型每层的输入输出、激活函数、模型的损失函数、优化方法,以及监控的指标等一系列网络搭建的必备步骤,可以说是十分简洁易操作了。

其次通过这个小例子,也能发现使用神经网络识别mnist数据集准确率还是非常高的,而且看loss下降图还未平稳,说明还有下降的空间,可以增加层数、或者增加每层的神经元、或者更改激活函数、损失函数等等来优化模型,这就是炼丹(调参)的内容了。