本系列相关答案和源代码托管在我的Github上:PY131/Machine-Learning_ZhouZhihua.
注:本题程实现基于python-theano(这里查看完整代码和数据集)。
卷积神经网络(Convolutional Neural Network, CNN)是“深度学习”的代表模型之一,是一种多隐层神经网络,正被广泛用于图像处理、语音识别等热点领域。
卷积神经网络的原理和特点,集中体现在以下三个核心思想当中:
在整合了上述三大特点之后,卷积神经网络具备了很强的畸变容忍能力,能够从复杂的对象中隐式地进行特征提取与学习。
卷积神经网络同多层感知机(MLP)一样,通过设置多个隐层来实现对复杂模型的学习。如下图所示是一个手写字符识别的卷积神经网络结构示意图(书p114):
从图中可以看到卷积层(convolutional layer)和采样层(pooling layer)的复合,其功能简述如下:
通过多层复合,隐层最终输出目标维特征向量,通过连接层和输出层输出结果。
神经网络的参数设计十分重要,关于CNN模型的一些参数的考虑(如隐层特征图数目和大小、滤波器大小等),可参考Convolutional Neural Networks (LeNet)文章最后Tips and Tricks的内容。
这里,我们采用python-theano深度学习库来实现基于MNIST数据的字符识别实验。关于theano的基础使用可参考:深度学习基础 - 基于Theano-MLP的字符识别实验(MNIST)或是Deep Learning Tutorials。
这里我们采用经过规约的数据集mnist.pkl.gz,给出该数据集的部分信息如下:
下面是一些样例图片:
通过对数据集的分析,确定此处该数据集已无需额外的预处理即可使用,只是在使用时注意维度变换即可。
基于theano来训练一个卷积神经网络需要完成的内容包括:
theano.shared
来达到权值共享,基于数据信息设计相关参数(隐层规模、滤波器大小、学习率、迭代次数限、若采样MSGD算法还需设置mini-batch大小等);theano.function
实现tanh/sigmoid、似然损失函数等;theano.tensor.signal.conv2d
实现二维(2D)卷积;采用theano.tensor.signal.pool.pool_2d
实现最大池化(max-pooling),进一步地:
class LeNetConvPoolLayer
);这里还需进一步说明各层规模和滤波器大小的设置:
以当前样本为例,输入层大小[28*28]
,若采用5*5
的滤波器进行卷积,则第一个卷积层的特征图大小为[24*24]
(ps. 28-5+1=24),若紧接着的亚采样模版大小为[2*2]
,那么该池化层特征图大小为[12*12]
(ps. 24/2=12)。同理,可计算出下一个卷积池化的特征图大小为[8*8]
和[4*4]
,再往后就只需要一个面向连接层的一维卷积层了,其节点数为当前的feature maps
数。然后按照MLP模型给出连接层和输出层即可。
各层规模设置的样例程序如下:
layer1 = LeNetConvPoolLayer(
rng,
input=layer0.output,
image_shape=(batch_size, nkerns[0], 12, 12),
filter_shape=(nkerns[1], nkerns[0], 5, 5),
poolsize=(2, 2)
)
layer2_input = layer1.output.flatten(2)
# construct a fully-connected sigmoidal layer
layer2 = HiddenLayer(
rng,
input=layer2_input,
n_in=nkerns[1] * 4 * 4,
n_out=500,
activation=T.tanh
)
# classify the values of the fully-connected sigmoidal layer
layer3 = LogisticRegression(input=layer2.output, n_in=500, n_out=10)
给出该训练程序简化样例如下查看完整程序:
def evaluate_lenet5(learning_rate=0.1, # 学习率
n_epochs=200, # 迭代批数
dataset='mnist.pkl.gz', # 数据集文件
nkerns=[20, 50], # 每隐层特征图数目序列
batch_size=500): # mini-batch大小(for MSGD)
# 加载数据,生成训练集/验证集/测试集
datasets = load_data(dataset)
train_set_x, train_set_y = datasets[0]
valid_set_x, valid_set_y = datasets[1]
test_set_x, test_set_y = datasets[2]
...
# 搭建模型网络结构(包括上面的隐层sizes推导)
# 输入
layer0_input = x.reshape((batch_size, 1, 28, 28))
# 第一层 - 复合
layer0 = LeNetConvPoolLayer(
rng,
input=layer0_input,
image_shape=(batch_size, 1, 28, 28),
filter_shape=(nkerns[0], 1, 5, 5),
poolsize=(2, 2)
)
# 第二层 - 复合
layer1 = LeNetConvPoolLayer( ... )
# 第三层 - 隐层
layer2_input = layer1.output.flatten(2)
layer2 = HiddenLayer( ... )
# 全连接输出
layer3 = LogisticRegression(input=layer2.output, n_in=500, n_out=10)
# 似然损失函数
cost = layer3.negative_log_likelihood(y)
...
# 参数更新机制
updates = [
(param_i, param_i - learning_rate * grad_i)
for param_i, grad_i in zip(params, grads)
]
# 模型训练函数体
train_model = theano.function(
[index],
cost,
updates=updates,
givens={
x: train_set_x[index * batch_size: (index + 1) * batch_size],
y: train_set_y[index * batch_size: (index + 1) * batch_size]
}
)
########## 模型训练 ##########
# 早停机制设置
patience = 10000 # 迭代次数耐心上限
patience_increase = 2 # 耐心上限拓展步长
improvement_threshold = 0.995 # 精度明显提升判断
# 验证周期
validation_frequency = min(n_train_batches, patience // 2)
...
# 循环
while (epoch < n_epochs) and (not done_looping):
epoch = epoch + 1
# mini-batch迭代
for minibatch_index in range(n_train_batches):
...
# 模型训练(损失计算+参数更新)
cost_ij = train_model(minibatch_index)
# 模型验证(a batch训练完成)
if (iter + 1) % validation_frequency == 0:
# 计算0-1损失 - 验证误差
validation_losses = [validate_model(i) for i in range(n_valid_batches)]
this_validation_loss = numpy.mean(validation_losses)
...
# 如果取得更好模型(验证精度提升)
if this_validation_loss < best_validation_loss:
# 若精度提升明显,但耐心迭代次数上限达到,则提高迭代次数上限
if this_validation_loss < best_validation_loss * improvement_threshold:
patience = max(patience, iter * patience_increase)
...
# 进行测试(在验证精度提升时)以方便我们对比观测
test_losses = [
test_model(i)
for i in range(n_test_batches)
]
test_score = numpy.mean(test_losses)
...
# 早停判断
if patience <= iter:
done_looping = True
break
...
#返回所需信息
这里采用MSGD(块随机梯度下降法)进行迭代寻优,下图是经过大约5万次迭代训练后得到的三种误差(训练/验证/测试)收敛曲线,可以看出其过程收敛性:
显示出一些测试样本的预测结果如下图示:
最终的运行结果打印如下:
最优验证误差结果: Best validation score of 1.080000 %
测试误差结果: Test performance 1.030000 %
过程时耗: The code for file Mnist_CNN.py ran for 90.23m
从这里的结果可以看出:一方面,卷积神经网络训练计算规模庞大(当前软硬件环境下耗时一个半小时);另一方面,得到的模型精度很高(在测试集上实现了约99%的精度,这基本意味着MNIST问题得到了解决)。
通过该实验,我们注意到:
通过该实验,我们回顾了卷积神经网络及其所代表的深度学习概念,练习了基于python-theano计算框架下的机器学习建模方法,为进一步的学习研究积累的实践经验。
下面列出相关参考: