手写数字识别——利用keras高层API快速搭建并优化网络模型

《手写数字识别——手动搭建全链接层》一文中,咱们经过机器学习的基本公式构建出了一个网络模型,其实现过程毫无疑问是过于复杂了——不得不考虑诸如数据类型匹配、梯度计算、准确度的统计等问题,可是这样的实践对机器学习的理解是大有裨益的。在大多数状况下,咱们仍是但愿能多简单就多简单地去搭建网络模型,这同时也算对得起TensorFlow这个强大的工具了。本节,仍是以手写数据集MNIST为例,利用TensorFlow2.0的keras高层API重现以前的网络。html

 

1、数据的导入与预处理

 


 关于这个过程,与上节讲过的相似,就再也不赘述了。须要提一点的就是,为了程序的整洁,将数据类型的转换过程单独写成一个预处理函数preprocess,经过Dataset对象的map方法应用该预处理函数。整个数据导入与预处理代码以下:python

import tensorflow as tf
from tensorflow.keras import datasets,optimizers,Sequential,metrics,layers

# 改变数据类型
def preprocess(x,y):
    x = tf.cast(x,dtype=tf.float32)/255-0.5
    x = tf.reshape(x,[-1,28*28])
    y = tf.one_hot(y, depth=10)
    y = tf.cast(y, dtype=tf.int32)
    return x,y

#60k 28*28
(train_x,train_y),(val_x,val_y) = datasets.mnist.load_data()

#生成Dataset对象
train_db = tf.data.Dataset.from_tensor_slices((train_x,train_y)).shuffle(10000).batch(256)
val_db = tf.data.Dataset.from_tensor_slices((val_x,val_y)).shuffle(10000).batch(256)

#预处理,对每一个数据应用preprocess
train_db = train_db.map(preprocess)
val_db = val_db.map(preprocess)

 

 

 

 

2、模型构建

 


 对于全链接层,keras提供了layers.Dense(units,activation)接口,利用它能够创建一层layer,多层堆叠放入keras提供的Sequential容器中,就造成了一个网络模型。在Dense的参数中,units决定了这层layer含有的神经元数量,activation是激活函数的选择。同以前的网络同样,咱们的网络传播能够看作是:input(784 units)->layer1(256 units)->ReLu->layer2(128 units)->ReLu->output(10 units)。所以,在Sequential容器中定义后三层,activation指定为ReLu,而输入层须要经过build时候指定input_shape来告诉网络输入层的神经元数量。构建的代码以下,经过summary方法能够打印网络信息。git

#网络模型
model = Sequential([
    layers.Dense(256,activation=tf.nn.relu),
    layers.Dense(128,activation=tf.nn.relu),
    layers.Dense(10),
])
#input_shape=(batch_size,input_dims)
model.build(input_shape=(None,28*28))
model.summary()

 

 

 

 

3、模型的训练

 


模型的训练最重要的就是权重更新和准确度统计。keras提供了多种优化器(optimizer)用于更新权重。优化器实际就是不一样的梯度降低算法,缓解了传统梯度降低可能没法收敛到全局最小值的问题。在上一节中就稍加讨论了三种。这里就简单对比一下一些优化器,至于详细的区别从此有时间再写篇随笔专门讨论:算法

  1. SGD:TensorFlow2.0 SGD实际是随机梯度降低+动量的综合优化器。随机梯度降低是每次更新随机选取一个样本计算梯度,这样计算梯度快不少,但怕大噪声;动量是在梯度降低的基础上,累计历史梯度信息加速梯度降低,这是由于一方面它想水稀释牛奶同样,能减少随机梯度降低对噪声的敏感度,另外一方面动量赋予降低以惯性,能够预见梯度变化。这优化器实话说让我联想到了PID控制。SGD须要指定学习率和动量大小。通常地,动量大小设置为0.9。
  2. Adagrad:采用自适应梯度的优化器。所谓自适应梯度,就是根据参数的频率,对每一个参数应用不一样的学习速率。可是该算法在迭代次数变得很大时,学习速率会变得很小,致使不能继续更新。Adagrad要求指定初始化的学习速率、累加器初始值和防止分母为0的偏置值。
  3. Adadelta:采用自适应增量的优化器。解决了adagrad算法学习速率消失的问题。Adagrad要求指定初始化的学习速率、衰减率和防止分母为0的偏置值。这个衰减率跟动量差很少,通常也指定为0.9。
  4. RMSprop:相似于Adadelta。
  5. Adam:采用梯度的一阶和二阶矩来估计更新参数。它结合了Adadelta和RMSprop的优势。能够说,深度学习一般都会选择Adam优化器。TensorFlow中,Adam优化器须要指定4个参数,但经验证实,它的默认参数能表现出很好的效果。

鉴于以上对比,此处选用Adam做为优化器,并采用其默认参数。api

除了梯度降低,还须要考虑的是Loss的计算方法。以前,咱们采用的是预测几率与实际值的差平方的均值,专业名称应该是欧几里得损失函数。其实,这是个错误,欧几里得损失函数适用于二元分类,多元分类应该采用交叉熵损失函数。有时候针对多元函数,咱们会很不自觉地想把输出层归一化,因而会在输出层以后,交叉熵计算前先softmax一下。可是因为softmax是采用指数形式进行计算的,若是输出各种几率相差较大,则大几率在归一化后几乎为1,小几率归一化以后几乎为0。为了不这一问题,一般是去掉softmax,在交叉熵函数tf.losses.CategoricalCrossentropy的参数中指from_logits=True。网络

Loss函数和优化器配置均可以经过compile方法指定,同时,还能够指定metrics列表来决定须要自动计算的信息,如准确度。机器学习

经过fit方法能够传入训练数据和测试数据。代码以下:函数

#配合Adam优化器、交叉熵Loss函数、metrics列表
model.compile(optimizer=optimizers.Adam(),
                loss=tf.losses.CategoricalCrossentropy(from_logits=True),
                metrics=['accuracy'])
#数据传入,迭代10次train_db,每迭代1次,计算一次测试数据集准确度
model.fit(train_db,epochs=10,validation_data=val_db,validation_freq=1)

 

以上创建的网络模型在第一次train_db迭代完后就能够达到0.8以上的准确度,并且这个迭代每次仅花费3秒左右。通过大约50次迭代,准确度就能够高达0.98!而经过上一节的方式,要达到这样的准确度,起码得训练半个小时。这其中最主要的差异就在于梯度降低算法的优化。工具

 

 

 

 

4、完整代码

 


 

 1 import tensorflow as tf
 2 from tensorflow.keras import datasets,optimizers,Sequential,metrics,layers
 3 
 4 # 改变数据类型
 5 def preprocess(x,y):
 6     x = tf.cast(x,dtype=tf.float32)/255-0.5
 7     x = tf.reshape(x,[-1,28*28])
 8     y = tf.one_hot(y, depth=10)
 9     y = tf.cast(y, dtype=tf.int32)
10     return x,y
11 
12 #60k 28*28
13 (train_x,train_y),(val_x,val_y) = datasets.mnist.load_data()
14 
15 #生成Dataset对象
16 train_db = tf.data.Dataset.from_tensor_slices((train_x,train_y)).shuffle(10000).batch(256)
17 val_db = tf.data.Dataset.from_tensor_slices((val_x,val_y)).shuffle(10000).batch(256)
18 
19 #预处理,对每一个数据应用preprocess
20 train_db = train_db.map(preprocess)
21 val_db = val_db.map(preprocess)
22 
23 #网络模型
24 model = Sequential([
25     layers.Dense(256,activation=tf.nn.relu),
26     layers.Dense(128,activation=tf.nn.relu),
27     layers.Dense(10),
28 ])
29 #input_shape=(batch_size,input_dims)
30 model.build(input_shape=(None,28*28))
31 model.summary()
32 
33 #配合Adam优化器、交叉熵Loss函数、metrics列表
34 model.compile(optimizer=optimizers.Adam(),
35                 loss=tf.losses.CategoricalCrossentropy(from_logits=True),
36                 metrics=['accuracy'])
37 #数据传入,迭代10次train_db,每迭代1次,计算一次测试数据集准确度
38 model.fit(train_db,epochs=100,validation_data=val_db,validation_freq=5)
相关文章
相关标签/搜索