keras入门实战:手写数字识别

来源:互联网 发布:相机数据恢复 编辑:程序博客网 时间:2024/05/29 18:46

【声明】本文是blog的翻译和个人的学习笔记

导读

近些年由于理论知识的硬件的快速发展,使得深度学习达到了空前的火热。深度学习已经在很多方面都成功得到了应用,尤其是在图像识别和分类领域,机器识别图像的能力甚至超过了人类。

本文用深度学习Python库Keras实现深度学习入门教程mnist手写数字识别。mnist手写数字识别是机器学习和深度学习领域的“hello world”,MNIST数据集是手写数字的数据集合,训练集规模为60000,测试集为10000。

本文的内容包括:

  • 如何用Keras加载MNIST数据集
  • 对于MNIST问题如何实现一个baseline的神经网络
  • 基于MNIST问题如何实现并评价一个卷积神经网络(CNN)
  • 基于MNIST问题如何实现一个接近最高准确率的深度学习模型

MNIST手写数字识别问题

MNIST问题是由Yann LeCun, Corinna Cortes 和Christopher Burges为了评估机器学习模型而设立的。问题的数据集是从一些National Institute of Standards and Technology (NIST)的文档中得来,是计算机视觉入门级的数据集,它包含各种手写数字图片:
这里写图片描述

它也包含每一张图片对应的标签,告诉我们这个是数字几。比如,上面这四张图片的标签分别是5,0,4,1。
每张图片是28*28像素(共784个像素)。对于一般的图片像素通道通常是3维,即rgb,代表red、green、blue三个颜色通道,而MNIST数据集的像素通道只有一位即为灰度值,每一个像素值在0到1之间表示这个像素的灰度,0表示白色,1表示黑色。图片的类别标签是这个图片的数字,取值范围为0-9.因此MNIST问题是一个多分类的问题,类别为10。
现在好的分类结果可是使错误率降到1%以下。接近最好效果的错误率大约为0.2%,可用大规模的CNN实现。

加载MNIST数据集

Keras提供了实现深度学习所需要的绝大部分函数库,可实现多种神经网络模型,并可加载多种数据集来评价模型的效果。下面的代码会自动加载数据,如果是第一次调用,数据会保存在你的hone目录下~/.keras/datasets/mnist.pkl.gz,大约15MB。

# Plot ad hoc mnist instancesfrom keras.datasets import mnistimport matplotlib.pyplot as plt# load (downloaded if needed) the MNIST dataset(X_train, y_train), (X_test, y_test) = mnist.load_data()# plot 4 images as gray scaleplt.subplot(221)plt.imshow(X_train[0], cmap=plt.get_cmap('gray'))plt.subplot(222)plt.imshow(X_train[1], cmap=plt.get_cmap('gray'))plt.subplot(223)plt.imshow(X_train[2], cmap=plt.get_cmap('gray'))plt.subplot(224)plt.imshow(X_train[3], cmap=plt.get_cmap('gray'))# show the plotplt.show()

上面的代码加载了数据集并画出了前4个图片:
这里写图片描述

多层感知机的baseline模型

在实现卷积神经网络这种复杂的模型之前,先实现一个简单但效果也不错的模型:多层感知机。这种模型也叫含隐层的神经网络。模型的效果可以使错误率达到1.87%。
第一步是加载所需要的库

import numpyfrom keras.datasets import mnistfrom keras.models import Sequentialfrom keras.layers import Densefrom keras.layers import Dropoutfrom keras.utils import np_utils

设定随机数种子,保证结果的可重现性

seed = 7numpy.random.seed(seed)

加载数据

(X_train, y_train), (X_test, y_test) = mnist.load_data()

数据集是3维的向量(instance length,width,height).对于多层感知机,模型的输入是二维的向量,因此这里需要将数据集reshape,即将28*28的向量转成784长度的数组。可以用numpy的reshape函数轻松实现这个过程。

num_pixels = X_train.shape[1] * X_train.shape[2]X_train = X_train.reshape(X_train.shape[0], num_pixels).astype('float32')X_test = X_test.reshape(X_test.shape[0], num_pixels).astype('float32')

给定的像素的灰度值在0-255,为了使模型的训练效果更好,通常将数值归一化映射到0-1。

X_train = X_train / 255X_test = X_test / 255

最后,模型的输出是对每个类别的打分预测,对于分类结果从0-9的每个类别都有一个预测分值,表示将模型输入预测为该类的概率大小,概率越大可信度越高。由于原始的数据标签是0-9的整数值,通常将其表示成0ne-hot向量。如第一个训练数据的标签为5,one-hot表示为[0,0,0,0,0,1,0,0,0,0]。

y_train = np_utils.to_categorical(y_train)y_test = np_utils.to_categorical(y_test)num_classes = y_test.shape[1]

现在需要做得就是搭建神经网络模型了,创建一个函数,建立含有一个隐层的神经网络。

# define baseline modeldef baseline_model():    # create model    model = Sequential()    model.add(Dense(num_pixels, input_dim=num_pixels, kernel_initializer='normal', activation='relu'))    model.add(Dense(num_classes, kernel_initializer='normal', activation='softmax'))    # Compile model    model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])    return model

模型的隐含层含有784个节点,接受的输入长度也是784(28*28),最后用softmax函数将预测结果转换为标签的概率值。
将训练数据fit到模型,设置了迭代轮数,每轮200个训练样本,将测试集作为验证集,并查看训练的效果。

# build the modelmodel = baseline_model()# Fit the modelmodel.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=10, batch_size=200, verbose=2)# Final evaluation of the modelscores = model.evaluate(X_test, y_test, verbose=0)print("Baseline Error: %.2f%%" % (100-scores[1]*100))

训练和测试结果如下:

Train on 60000 samples, validate on 10000 samples
Epoch 1/10
6s - loss: 0.2789 - acc: 0.9210 - val_loss: 0.1416 - val_acc: 0.9578
Epoch 2/10
5s - loss: 0.1117 - acc: 0.9677 - val_loss: 0.0917 - val_acc: 0.9707
Epoch 3/10
5s - loss: 0.0717 - acc: 0.9796 - val_loss: 0.0787 - val_acc: 0.9767
Epoch 4/10
6s - loss: 0.0502 - acc: 0.9859 - val_loss: 0.0741 - val_acc: 0.9767
Epoch 5/10
5s - loss: 0.0372 - acc: 0.9890 - val_loss: 0.0681 - val_acc: 0.9788
Epoch 6/10
5s - loss: 0.0269 - acc: 0.9925 - val_loss: 0.0625 - val_acc: 0.9808
Epoch 7/10
5s - loss: 0.0208 - acc: 0.9948 - val_loss: 0.0619 - val_acc: 0.9814
Epoch 8/10
6s - loss: 0.0140 - acc: 0.9970 - val_loss: 0.0639 - val_acc: 0.9799
Epoch 9/10
5s - loss: 0.0108 - acc: 0.9978 - val_loss: 0.0597 - val_acc: 0.9812
Epoch 10/10
5s - loss: 0.0080 - acc: 0.9985 - val_loss: 0.0591 - val_acc: 0.9813
Baseline Error: 1.87%

简单的卷积神经网络

前面介绍了如何加载训练数据并实现一个简单的单隐层神经网络,并在测试集上取得了不错的效果。现在要实现一个卷积神经网络,想要在MNIST问题上取得更好的效果。

卷积神经网络(CNN)是一种深度神经网络,与单隐层的神经网络不同的是它还包含卷积层、池化层、Dropout层等,这使得它在图像分类的问题上有更优的效果。详细的CNN教程可以参见斯坦福大学的cs231n课程讲义,中文版链接

第一步依然是导入需要的函数库

import numpyfrom keras.datasets import mnistfrom keras.models import Sequentialfrom keras.layers import Densefrom keras.layers import Dropoutfrom keras.layers import Flattenfrom keras.layers.convolutional import Conv2Dfrom keras.layers.convolutional import MaxPooling2Dfrom keras.utils import np_utilsfrom keras import backend as KK.set_image_dim_ordering('th')

设定随机数种子

seed = 7numpy.random.seed(seed)

将数据集reshape,CNN的输入是4维的张量(可看做多维的向量),第一维是样本规模,第二维是像素通道,第三维和第四维是长度和宽度。并将数值归一化和类别标签向量化。

# load data(X_train, y_train), (X_test, y_test) = mnist.load_data()# reshape to be [samples][pixels][width][height]X_train = X_train.reshape(X_train.shape[0], 1, 28, 28).astype('float32')X_test = X_test.reshape(X_test.shape[0], 1, 28, 28).astype('float32')X_train = X_train / 255X_test = X_test / 255# one hot encode outputsy_train = np_utils.to_categorical(y_train)y_test = np_utils.to_categorical(y_test)num_classes = y_test.shape[1]

接下来构造CNN。

  1. 第一层是卷积层。该层有32个feature map,或者叫滤波器,作为模型的输入层,接受[pixels][width][height]大小的输入数据。feature map的大小是5*5,其输出接一个‘relu’激活函数。
  2. 下一层是pooling层,使用了MaxPooling,大小为2*2。
  3. 下一层是Dropout层,该层的作用相当于对参数进行正则化来防止模型过拟合。
  4. 接下来是全连接层,有128个神经元,激活函数采用‘relu’。
  5. 最后一层是输出层,有10个神经元,每个神经元对应一个类别,输出值表示样本属于该类别的概率大小。
def baseline_model():    # create model    model = Sequential()    model.add(Conv2D(32, (5, 5), input_shape=(1, 28, 28), activation='relu'))    model.add(MaxPooling2D(pool_size=(2, 2)))    model.add(Dropout(0.2))    model.add(Flatten())    model.add(Dense(128, activation='relu'))    model.add(Dense(num_classes, activation='softmax'))    # Compile model    model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])    return model

接着开始训练模型

# build the modelmodel = baseline_model()# Fit the modelmodel.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=10, batch_size=200, verbose=2)# Final evaluation of the modelscores = model.evaluate(X_test, y_test, verbose=0)print("Baseline Error: %.2f%%" % (100-scores[1]*100))

训练和测试结果如下:

Train on 60000 samples, validate on 10000 samples
Epoch 1/10
137s - loss: 0.2329 - acc: 0.9340 - val_loss: 0.0820 - val_acc: 0.9742
Epoch 2/10
140s - loss: 0.0736 - acc: 0.9781 - val_loss: 0.0466 - val_acc: 0.9842
Epoch 3/10
138s - loss: 0.0531 - acc: 0.9839 - val_loss: 0.0432 - val_acc: 0.9860
Epoch 4/10
145s - loss: 0.0404 - acc: 0.9876 - val_loss: 0.0389 - val_acc: 0.9872
Epoch 5/10
135s - loss: 0.0335 - acc: 0.9893 - val_loss: 0.0341 - val_acc: 0.9886
Epoch 6/10
133s - loss: 0.0275 - acc: 0.9915 - val_loss: 0.0308 - val_acc: 0.9893
Epoch 7/10
133s - loss: 0.0233 - acc: 0.9926 - val_loss: 0.0363 - val_acc: 0.9880
Epoch 8/10
137s - loss: 0.0204 - acc: 0.9937 - val_loss: 0.0320 - val_acc: 0.9889
Epoch 9/10
139s - loss: 0.0167 - acc: 0.9945 - val_loss: 0.0294 - val_acc: 0.9893
Epoch 10/10
139s - loss: 0.0143 - acc: 0.9957 - val_loss: 0.0310 - val_acc: 0.9907
Baseline Error: 0.93%

可以看出相对于单隐层神经网络,CNN的效果有很大提升,error rate 从1.87%降到了0.93%。

上面实现了一个只含有一层卷积层和pooling层的CNN,为了实现更好的分类效果,可以添加多层的Convolution2D和MaxPooling2D,CNN会自动提取特征,学习到更好的分类效果。