独家 | 如何从头开始为MNIST手写数字分类建立卷积神经网络(附代码)

本教程详述如何利用Keras从头开始建立卷积神经网络(CNN),用于MNIST手写数字分类。内容涵盖数据集加载、预处理、模型构建、评估方法、基准模型的创建及其性能分析,还包括如何提升模型性能和保存最终模型以进行预测。通过学习,读者将理解如何开发和评估CNN模型,并实现99%以上的分类精度。
摘要由CSDN通过智能技术生成

640?wx_fmt=png

翻译:张睿毅

校对:吴金笛

本文约9300字,建议阅读20分钟。

本文章逐步介绍了卷积神经网络的建模过程,最终实现了MNIST手写数字分类。


MNIST手写数字分类问题是计算机视觉和深度学习中使用的标准数据集。


尽管数据集得到了有效的解决,但它可以作为学习和实践如何开发、评估和使用卷积深度学习神经网络从头开始进行图像分类的基础。这包括如何开发一个用于评估模型性能的强大测试工具,如何探索模型的改进,以及如何保存模型,然后加载它以对新数据进行预测。


在本教程中,您将了解如何从头开始开发用于手写数字分类的卷积神经网络。


完成本教程后,您将了解:


  • 如何开发测试工具以开发稳健的模型评估并建立分类任务性能的基准线

  • 如何在基准模型上拓展以改进学习及模型容量

  • 如何开发最终模型,评估最终模型性能,并用它对于新图像进行预测


让我们开始吧!


640?wx_fmt=png

Photo by Richard Allaway, some rights reserved


图片来源:https://www.flickr.com/photos/geographyalltheway_photos/2918451763/


教程概览


教程的五个部分分别是:


1. MNIST手写数字分类数据集

2. 模型评估方法

3. 如何建立基准模型

4. 如何建立改进模型

5. 如何完成模型建立并进行预测


1. MNIST 手写数字分类数据集


MNIST数据集是修改后的国家标准与技术研究所数据集的缩写。


它是一个由60000个28×28像素的小正方形灰度图像组成的数据集,这些图像的手写单位数介于0和9之间。


任务是将手写数字的给定图像分类为10个类中的一个,这些类表示0到9之间的整数值(包括0和9)。


它是一个广泛使用和深入理解的数据集,并且在大多数情况下是“已解决”的。性能最好的模型是深度学习卷积神经网络,其分类精度达到99%以上,保留测试数据集的错误率在0.4%到0.2%之间。


下面的示例使用keras API加载MNIST数据集,并创建训练数据集中前九个图像的绘图。


# example of loading the mnist dataset

from keras.datasets import mnist

from matplotlib import pyplot

# load dataset

(trainX, trainy), (testX, testy) = mnist.load_data()

# summarize loaded dataset

print('Train: X=%s, y=%s' % (trainX.shape, trainy.shape))

print('Test: X=%s, y=%s' % (testX.shape, testy.shape))

# plot first few images

for i in range(9):

# define subplot

pyplot.subplot(330 + 1 + i)

# plot raw pixel data

pyplot.imshow(trainX[i], cmap=pyplot.get_cmap('gray'))

# show the figure

pyplot.show()


运行该示例将加载MNIST训练和测试数据集并打印它们的形状。


我们可以看到训练数据集中有60000个例子,测试数据集中有10000个例子,图像确实是28×28像素的正方形。


Train: X=(60000, 28, 28), y=(60000,)Test: X=(10000, 28, 28), y=(10000,)


同时,我们创建了数据集中前九个图像的绘图,显示待分类图像的自然手写性质。


640?wx_fmt=jpeg

从MNIST数据集中选出的子集


2. 模型评估方法


尽管MNIST数据集得到了有效的解决,但使用卷积神经网络解决图像分类任务的方法可以作为开发和实践的一个有用的起点。


我们可以从头开始开发一个新的模型,而不是回顾数据集上性能良好的模型的文献。


数据集已经有了一个明确定义的训练和测试数据集,我们可以使用它。


为了估计给定训练运行模型的性能,我们可以进一步将训练集划分为训练和验证数据集。然后,可以绘制每次运行的训练和验证数据集的性能,以提供学习曲线,并洞察模型学习问题的程度。


keras API通过在训练模型时向 model.fit() 函数指定 “validation_data” 参数来支持这一点,该参数将返回一个对象,该对象描述了每个训练阶段所选损失和指标的模型性能。


# record model performance on a validation dataset during training

history = model.fit(..., validation_data=(valX, valY))


为了估计一个模型在一般问题上的性能,我们可以使用k倍交叉验证,或者5倍交叉验证。这将在训练和测试数据集的差异以及学习算法的随机性方面,给出一些模型的方差。考虑到标准差,模型的性能可以作为k-折叠的平均性能,如果需要,可以用它来估计置信区间。


我们可以使用scikit Learn API中的Kfold类来实现给定神经网络模型的k重交叉验证评估。虽然我们可以选择一种灵活的方法,其中kfold类只用于指定每个spit所用的行索引,但实现这一点的方法有很多种。


640?wx_fmt=png


我们将保留实际的测试数据集,并将其用作最终模型的评估。


3. 如何建立基准模型


第一步是建立一个基准模型。


这一点很关键,因为它即涉及到为测试工具开发基础设施,以便我们设计的任何模型都可以在数据集上进行评估,并且它在模型性能方面建立了一个基线,通过这个基线可以比较所有的改进。


测试工具的设计是模块化的,我们可以为每个部件开发单独的功能。如果我们愿意的话,这允许对测试工具的某个特定方面进行修改或相互更改,独立于其他部分。


我们可以用五个关键要素开发这个测试工具。它们是数据集的加载、数据集的准备、模型的定义、模型的评估和结果的表示。


加载数据集


我们对数据集已经有一些了解。


例如,我们知道图像都是预先对齐的(例如,每个图像只包含一个手绘数字),所有图像都具有相同的28×28像素的正方形大小,并且图像是灰度的。


因此,我们可以加载图像并将数据数组整形为具有单一颜色通道。


640?wx_fmt=png



# load dataset

(trainX, trainY), (testX, testY) = mnist.load_data()

# reshape dataset to have a single channel

trainX = trainX.reshape((trainX.shape[0], 28, 28, 1))

testX = testX.reshape((testX.shape[0], 28, 28, 1))


我们还知道有10个类,这些类被表示为唯一的整数。


因此,我们可以对每个样本的类元素使用一个热编码,将整数转换为一个10元素的二进制向量,其中1表示类值的索引,0表示所有其他类的值。我们可以使用to_categorial()实际程序函数来实现这一点。


640?wx_fmt=png


# one hot encode target values

trainY = to_categorical(trainY)

testY = to_categorical(testY)


load_dataset()函数实现这些行为,并可以被用作加载数据集。


640?wx_fmt=png


# load train and test dataset

def load_dataset():

# load dataset

(trainX, trainY), (testX, testY) = mnist.load_data()

# reshape dataset to have a single channel

trainX = trainX.reshape((trainX.shape[0], 28, 28, 1))

testX = testX.reshape((testX.shape[0], 28, 28, 1))

# one hot encode target values

trainY = to_categorical(trainY)

testY = to_categorical(testY)

return trainX, trainY, testX, testY


准备像素数据


我们知道,数据集中每个图像的像素值都是介于黑白或0到255之间的无符号整数。


我们不知道缩放用于建模的像素值的最佳方法,但我们知道需要进行一些缩放。


一个好的起点是规范化灰度图像的像素值,例如将其重新调整到范围[0,1]。这涉及到首先将数据类型从无符号整数转换为浮点数,然后将像素值除以最大值。


640?wx_fmt=png


# convert from integers to floats

train_norm = train.astype('float32')

test_norm = test.astype('float32')

# normalize to range 0-1

train_norm = train_norm / 255.0

test_norm = test_norm / 255.0


以下的prep_pixels()函数实现这些行为,我们提供这些从训练和测试数据集中需要被测量的像素值给这个函数。


640?wx_fmt=png


# scale pixels

def prep_pixels(train, test):

# convert from integers to floats

train_norm = train.astype('float32')

test_norm = test.astype('float32')

# normalize to range 0-1

train_norm = train_norm / 255.0

test_norm = test_norm / 255.0

# return normalized images

return train_norm, test_norm


这个函数必须被调用以在任何模型之前准备好像素值。


定义模型


接下来,我们需要为问题定义一个基线卷积神经网络模型。


该模型主要有两个部分:前端特征提取由卷积层和池化层组成,后端分类器进行预测。


对于卷积前端,我们可以从单个卷积层开始,该卷积层具有较小的过滤器大小(3,3)和少量的过滤器(32),然后是最大池化层。然后可以展平过滤器映射,为分类器提供特性。


考虑到该问题是一个多类分类任务,我们知道我们需要一个具有10个节点的输出层来预测属于这10个类中每个类的图像的概率分布。这还需要使用SoftMax激活功能。在特性提取器和输出层之间,我们可以添加一个全连接层来解释特性,在本例中是100个节点。


所有层都将使用relu激活函数和He 权重初始化方案,这两个都是最佳方法。


我们将对学习率为0.01,动量为0.9的随机梯度下降优化器使用保守配置。分类交叉熵损失函数将得到优化,适用于多类分类,我们将监测分类精度指标,这是适当的,因为我们在10个类中的每一类都有相同数量的例子。


下面的define_model()函数将定义并返回此模型。


640?wx_fmt=png


# define cnn model

def define_model():

model = Sequential()

model.add(Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_uniform', input_shape=(28, 28, 1)))

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

model.add(Flatten())

model.add(Dense(100, activation='relu', kernel_initializer='he_uniform'))

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

# compile model

opt = SGD(lr=0.01, momentum=0.9)

model.compile(optimizer=opt, loss='categorical_crossentropy', metrics=['accuracy'])

return model


评估模型


模型定义后,我们需要对其进行评估。


模型将通过五重交叉验证进行评估。选择k=5的值为重复评估提供基线,并且不需要太长的运行时间。每个测试集将是训练数据集的20%,或大约12000个示例,接近此问题的实际测试集大小。


训练数据集在分割前进行洗牌,每次都进行样本洗牌,这样我们评估的任何模型在每个折叠中都将具有相同的训练和测试数据集,从而提供模型之间的逐个比较。


我们将为一个适度的10个训练阶段培训基线模型,默认批量大小为32个示例。每个阶段的测试集将用于评估模型在训练运行的每个阶段,以便我们可以稍后创建学习曲线,并在运行结束时,以便我们可以评估模型的性能。因此,我们将跟踪每次运行的结果历史,以及折叠的分类精度。


下面的evaluate_model()函数实现了这些行为,将定义的模型和培训数据集作为参数,并返回一个精度分数和训练历史的列表,这些列表可以稍后进行总结。


640?wx_fmt=png


# evaluate a model using k-fold cross-validation

def evaluate_model(model, dataX, dataY, n_folds=5):

scores, histories = list(), list()

# prepare cross validation

kfold = KFold(n_folds, shuffle=True, random_state=1)

# enumerate splits

for train_ix, test_ix in kfold.split(dataX):

# select rows for train and test

trainX, trainY, testX, testY = dataX[train_ix], dataY[train_ix], dataX[test_ix], dataY[test_ix]</

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
利用tensorflow实现的卷积神经网络来进行MNIST手写数字图像的分类。 #导入numpy模块 import numpy as np #导入tensorflow模块,程序使用tensorflow来实现卷积神经网络 import tensorflow as tf #下载mnist数据集,并从mnist_data目录中读取数据 from tensorflow.examples.tutorials.mnist import input_data mnist = input_data.read_data_sets('mnist_data',one_hot=True) #(1)这里的“mnist_data” 是和当前文件相同目录下的一个文件夹。自己先手工建立这个文件夹,然后从https://yann.lecun.com/exdb/mnist/ 下载所需的4个文件(即该网址中第三段“Four files are available on this site:”后面的四个文件),并放到目录MNIST_data下即可。 #(2)MNIST数据集手写数字字符的数据集。每个样本都是一张28*28像素的灰度手写数字图片。 #(3)one_hot表示独热编码,其值被设为true。在分类问题的数据集标注时,如何不采用独热编码的方式, 类别通常就是一个符号而已,比如说是9。但如果采用独热编码的方式,则每个类表示为一个列表list,共计有10个数值,但只有一个为1,其余均为0。例如,“9”的独热编码可以为[00000 00001]. #定义输入数据x和输出y的形状。函数tf.placeholder的目的是定义输入,可以理解为采用占位符进行占位。 #None这个位置的参数在这里被用于表示样本的个数,而由于样本个数此时具体是多少还无法确定,所以这设为None。而每个输入样本的特征数目是确定的,即为28*28。 input_x = tf.placeholder(tf.float32,[None,28*28])/255 #因为每个像素的取值范围是 0~255 output_y = tf.placeholder(tf.int32,[None,10]) #10表示10个类别 #输入层的输入数据input_x被reshape成四维数据,其中第一维的数据代表了图片数量 input_x_images = tf.reshape(input_x,[-1,28,28,1]) test_x = mnist.test.images[:3000] #读取测试集图片的特征,读取3000个图片 test_y = mnist.test.labels[:3000] #读取测试集图片的标签。就是这3000个图片所对应的标签
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值