第三节 手写数字识别(1 / 1)

使用Keras的流程如图7-8所示。

图7-8 Keras使用的过程

(1)选择模型

Keras提供序贯式模型和函数式模型两种模型,在以下的例子中将使用序贯式模型。

(2)构建网络层

在以下的例子中使用了卷积网络层和丢弃层(Dropout)。

(3)编译

编译相当于进行搭建,可以使用model.compile( )函数体验其作用。

(4)训练

这是耗费时间的步骤,通过训练计算出所有的网络参数,对应的函数是model.fit( )。

(5)预测

这是使用模型解决问题的步骤。例如,一个用来识别花朵类型的网络,输入花朵图片之后,预测出该花朵的类别。

几乎所有智能手机都支持手写识别,即通过手指或手写笔在屏幕上进行书写,然后识别出所写的字符(图7-9)。下面以手写数字识别为例讲解上述流程并具体实现这个识别任务。

传统计算机视觉方法中,识别手写字体是通过定义特征完成的。例如,某个手写数字带有两个封闭区域,就很可能是8。这样的方法经过不断地特征总结,准确率可以超过90%,这已经达到了可以实际应用的水平。但是当需要识别的类别变得更多的时候,例如,从10个数字增加到3 900个汉字,如果依然通过总结特征进行识别,那么识别率的提升就需要巨大的工作量,也很难提高到可用的程度。

然而通过深度学习,手写识别变成了一项简单的工作。这里的简单是指构建过程简单,但完成这个识别任务的整个工作并不容易,因为深度学习需要做大量的标注训练数据的工作。对手写数字来说,所谓的标注数据,就是通过人工标注各个手写图片到底是什么数字,从而建立图片和数字之间的映射关系(表7-1)。

图7-9 手写数字图片

表7-1 图片标注

在这个案例中,使用的数据集是深度学习经常使用的数据集MINST。这是一个手写数字数据库,包含60 000个训练样本和10 000个测试样本,每个样本图像的宽高为28×28。需要注意的是,此数据集中的图片是以二进制存储的,不能直接以图像格式查看,不过很容易找到将其转换成图像格式的工具。

接下来的代码将引导读者下载数据、构建网络、训练网络。使用的网络是在处理图片数据时非常有效的卷积神经网络。尽管这个案例搭建网络的过程很简单,但是读者可以据此进行更深入地探索。这一节编写代码的方式与前几章稍有不同,可以把它们保存成.py文件后直接运行,其中每一部分的含义已经在相应位置做了详细注释。读者在掌握了基本方法后,还可以尝试对参数进行调整,并观察调整后的识别效果,从而进一步理解这种方法。

from __future__ import print_function

#导入这个模块是增加Python不同版本之间兼容性的一种做法

import keras

#导入keras模块

from keras.datasets import mnist

#导入数据库函数,因为国内访问亚马逊云速度较慢,也可以不用这种方式下

#载数据,而是从教材资源平台下载并读取数据

from keras.models import Sequential

#使用keras的序贯模型

from keras.layers import Dense,Dropout,Flatten

from keras.layers import Conv2D,MaxPooling2D

#导入需要使用的网络层,包括稠密层、Dropout层、压平层、二维卷积层、

#池化层

from keras import backend as K

#导入后端

batch_size=128

#控制每个训练批次的数据大小的参数,读者可以调整并观察会产生什么变化

num_classes=10

#分类器类别数量。因为要识别0到9共10个手写数字,所以类别是10

#如果识别手写小写字母,则分类就是26

epochs=12

#训练周期,读者可以调整并观察效果,训练周期越大,训练时间越长

img_rows,img_cols=28,28

#图片的长和宽的像素数

(x_train,y_train),(x_test,y_test)=mnist.load_data(path="D:\\mnist.npz")

#如果方便访问亚马逊云,可以用(x_train,y_train),(x_test,y_test)=

#mnist.load_data( )来自动下载数据。如果已经从教材资源平台将数据下载

#到本地,#假设保存在D盘根目录下,就使用上述命令形式。需要注意的是,

#为了和linux兼容,windows下的路径反斜杠"\"都要写成"\\"另外,

#上述命令将数据分为训练数据和验证数据。无论是训练集还是验证集,都令x

#是输入数据,y是输出数据

if K.image_data_format()=='channels_first':

x_train=x_train.reshape(x_train.shape[0],1,img_rows,img_cols)

x_test=x_test.reshape(x_test.shape[0],1,img_rows,img_cols)

input_shape=(1,img_rows,img_cols)

else:

x_train=x_train.reshape(x_train.shape[0],img_rows,img_cols,1)

x_test=x_test.reshape(x_test.shape[0],img_rows,img_cols,1)

input_shape=(img_rows,img_cols,1)

#因为Theano和TensorFlow定义的图片格式不同,这里针对不同的后台对

#数据进行处理,读者暂且可以不用深究

x_train=x_train.astype('float32')

x_test=x_test.astype('float32')

x_train/=255

x_test/=255

print('x_train shape:',x_train.shape)

print(x_train.shape[0],'train samples')

print(x_test.shape[0],'test samples')

#上述命令给出训练集和测试集的维度,输出如下:

#x_train shape:(60000,28,28,1)

#60000 train samples

#10000 test samples

y_train=keras.utils.to_categorical(y_train,num_classes)

y_test=keras.utils.to_categorical(y_test,num_classes)

#上述命令将训练数据和验证数据的类别转化成keras支持的格式

#通过以下代码构建深度网络,使用2D卷积层,池化层、Dropout层、压平

#层等

model=Sequential()

model.add(Conv2D(32,kernel_size=(3,3),

activation='relu',

input_shape=input_shape))

model.add(Conv2D(64,(3,3),activation='relu'))

model.add(MaxPooling2D(pool_size=(2,2)))

model.add(Dropout(0.25))

model.add(Flatten())

model.add(Dense(128,activation='relu'))

model.add(Dropout(0.5))

model.add(Dense(num_classes,activation='softmax'))

#上述深度网络包括两个卷积层,输出如下:

#WARNING:TensorFlow:From C:\Python\WinPython-64bit-3.6.1.0Q

#t5\Python-3.6.1.amd64\lib\site-packages\keras\backend\

#TensorFlow_backend.py:1062:calling reduce_prod(from

#TensorFlow.Python.ops.math_ops)with keep_dims is

#deprecated and will #be removed in a future version.

#Instructions for updating:

#keep_dims is deprecated,use keepdims instead

#这是一条警告命令,版本不同,警告信息可能也不同,提示内容是一些命令可

#能在后续版本中将不予支持

model.compile(loss=keras.losses.categorical_crossentropy,

optimizer=keras.optimizers.Adadelta(),

metrics=['accuracy'])

#使用以上代码进行网络具体搭建

#一般也会输出类似的警告信息

model.fit(x_train,y_train,

batch_size=batch_size,

epochs=epochs,

verbose=1,

validation_data=(x_test,y_test))

#使用上述代码进行模型训练,训练时长根据配置不同而不同

#训练过程中会给出每次更新网络参数所需的时间、损失、精度等

#因为训练过程有随机因素,所以读者训练的结果也许会有差别,不必纠结

Train on 60000 samples,validate on 10000 samples

Epoch 1/12

60000/60000[==============================]-131s-loss:0.3303-acc:0.8998-val_loss:0.0758-val_acc:0.9766

Epoch 2/12

60000/60000[==============================]-9s-loss:

0.1106-acc:0.9676-val_loss:0.0522-val_acc:0.9825

Epoch 3/12

60000/60000[==============================]-9s-loss:

0.0831-acc:0.9746-val_loss:0.0405-val_acc:0.9870

Epoch 4/12

60000/60000[==============================]-9s-loss:0.0677-acc:0.9798-val_loss:0.0360-val_acc:0.9873

Epoch 5/12

60000/60000[==============================]-9s-loss:0.0595-acc:0.9821-val_loss:0.0353-val_acc:0.9875

Epoch 6/12

60000/60000[==============================]-10s-loss:0.0556-acc:0.9837-val_loss:0.0327-val_acc:0.9891

Epoch 7/12

60000/60000[==============================]-12s-loss:0.0481-acc:0.9855-val_loss:0.0292-val_acc:0.9896

Epoch 8/12

60000/60000[==============================]-14s-loss:0.0453-acc:0.9860-val_loss:0.0299-val_acc:0.9897

Epoch 9/12

60000/60000[==============================]-17s-loss:0.0420-acc:0.9868-val_loss:0.0297-val_acc:0.9899

Epoch 10/12

60000/60000[==============================]-17s-loss:0.0395-acc:0.9878-val_loss:0.0289-val_acc:0.9904

Epoch 11/12

60000/60000[==============================]-17s-loss:0.0376-acc:0.9885-val_loss:0.0278-val_acc:0.9914

Epoch 12/12

60000/60000[==============================]-15s-loss:0.0357-acc:0.9885-val_loss:0.0268-val_acc:0.9909

#训练结果如下。同样,不同的配置和训练过程会导致总损失不同

#读者实践过程中获得的数值一般不会与教材的示例完全一致

score=model.evaluate(x_test,y_test,verbose=0)

print('Test loss:',score[0])

#使用总损失对模型进行评估,即评估该模型的效果,通过总损失表示

在本教材使用的计算机上,该训练样本的损失如下。

Test loss:0.0268492490485