TensorFlow layers
模块提供了一个高级API,可以轻松构建神经网络。它提供了便于创建密集(完全连接)层和卷积层,添加激活函数以及应用缺陷正则化的方法。在本教程中,您将学习如何layers
构建卷积神经网络模型来识别MNIST数据集中的手写数字。
所述MNIST数据集包括60000个训练样例和手写数字0-9,格式为28x28像素的单色图像万个测试实施例。
我们设计TensorFlow程序的骨架。创建一个调用的文件 cnn_mnist.py
,并添加以下代码:
在您完成本教程时,您将添加代码来构建,训练和评估卷积神经网络。完整的最终代码可以在 这里找到。
卷积神经网络简介
卷积神经网络(CNN)是用于图像分类任务的当前最先进的模型架构。CNN将一系列过滤器应用于图像的原始像素数据,以提取和学习较高级别的功能,然后模型可用于分类。CNN包含三个组成部分:
-
卷积层,将图像的指定数量的卷积滤波器应用。对于每个子区域,层执行一组数学运算,以在输出特征图中产生单个值。卷积层通常将 ReLU激活功能应用于输出,以将非线性引入到模型中。
-
汇集层,其 对 由卷积层提取的图像数据进行下采样,以降低特征图的维度,以减少处理时间。通常使用的池化算法是最大池,其提取特征映射的子区域(例如,2×2像素块),保持其最大值,并丢弃所有其他值。
-
密集(完全连接)层,对卷积层提取的特征进行分类,并由池层进行下采样。在密集层中,层中的每个节点连接到上一层中的每个节点。
通常,CNN由执行特征提取的卷积模块组成。每个模块由一个卷积层组成,后面是一个池层。最后一个卷积模块后面是一个或多个执行分类的密集层。CNN中的最终密集层包含模型中每个目标类的单个节点(模型可以预测的所有可能的类),其中 softmax激活函数为每个节点生成0-1之间的值这些softmax值等于1)。我们可以将给定图像的softmax值解释为图像落入每个目标类的可能性的相对度量。
注意:对于CNN架构的更全面的演练,请参阅斯坦福大学的 视觉识别卷积神经网络课程材料。
构建CNN MNIST分类器
我们使用以下CNN架构构建一个模型来对MNIST数据集中的图像进行分类:
- 卷积层#1:应用32个5x5滤镜(提取5x5像素的子区域),具有ReLU激活功能
- 池化层#1:使用2x2过滤器执行最大池化,步长为2(指定池区域不重叠)
- 卷积层#2:应用64个5x5滤镜,具有ReLU激活功能
- 集合层#2:再次,使用2x2过滤器执行最大池,并且步长为2
- 密集层#1:1,024个神经元,脱落正则化率为0.4(在训练期间任何给定元素的概率为0.4)
- 密集层#2(Logit Layer):10个神经元,每个数字目标类(0-9)一个。
该tf.layers
模块包含创建上述三种类型中的每一种的方法:
conv2d()
。构造二维卷积层。获取过滤器的数量,过滤内核大小,填充和激活功能作为参数。
max_pooling2d()
。使用max-pooling算法构建二维池化层。将过滤器大小合并为一个参数。
dense()
。构造一个致密层。将神经元数量和激活函数作为参数。
这些方法中的每一个都接受张量作为输入,并将转换的张量作为输出返回。这样可以轻松地将一层连接到另一层:只需从一个层创建方法获取输出,并将其作为输入提供给另一层。
打开cnn_mnist.py
并添加以下cnn_model_fn
功能,它符合TensorFlow的Estimator API预期的界面(稍后在创建估计器中)。cnn_mnist.py
取MNIST特征数据,标签和 模型模式(TRAIN
,EVAL
, INFER
)作为参数; 配置CNN; 并返回预测,损失和培训操作:
以下部分(与上面的每个代码块对应的标题)深入了解tf.layers
用于创建每个图层的代码,以及如何计算损失,配置训练操作和生成预测。如果您已经有CNN和TensorFlow的Estimator
经验,并且直观地找到上述代码,您可能需要略过这些部分,或者跳过“训练和评估CNN MNIST分类器”。
输入层
layers
用于为二维图像数据创建卷积和合并图层的模块中的方法期望输入张量具有如下定义的形状 :[batch_size,image_width, image_height, channels]
batch_size
。在训练期间执行梯度下降时使用的示例子集的大小。
image_width
。示例图像的宽度。
image_height
。示例图像的高度。
channels
。示例图像中的颜色通道数。对于彩色图像,通道数为3(红,绿,蓝)。对于单色图像,只有1个通道(黑色)。
在这里,我们的MNIST数据集由单色的28x28像素图像组成,因此我们的输入层所需的形状是。[batch_size, 28, 28, 1]
要将我们的输入要素map(features
)转换为此形状,我们可以执行以下reshape
操作:
请注意,我们已经指出-1
了批量大小,其中规定了此维度应根据输入值的数量进行动态计算 features
,并保持所有其他维度的大小不变。这允许我们将其视为batch_size
可以调谐的超参数。例如,如果我们以5的批次向我们的模型中提供示例,features
将包含3,920个值(每个图像中每个像素的一个值),并且input_layer
将具有一个形状 [5, 28, 28, 1]
。同样地,如果我们以100个批次的方式提供示例,features
将包含78,400个值,并且input_layer
将具有一个形状[100, 28, 28, 1]
。
卷积层#1
在我们的第一个卷积层中,我们要对输入层应用32个5x5滤波器,并具有一个ReLU激活功能。我们可以使用模块中的conv2d()
方法 layers
来创建这个层,如下所示:
该inputs
参数指定了输入张量,其中必须有形状 。在这里,我们将我们的第一个卷积层连接起来,它具有形状。[batch_size, image_width, image_height, channels]
input_layer
[batch_size, 28, 28, 1]
注意:将接受传递参数时 的形状 。
conv2d()
[channels, batch_size, image_width,image_height]
data_format=channels_first
所述filters
参数指定的过滤器,以应用(这里,32)的数量,并且 kernel_size
指定了作为过滤器的尺寸(此处,)。[width, height]
[5, 5]
提示:如果过滤器宽度和高度具有相同的值,则可以指定kernel_size
-eg 的单个整数kernel_size=5
。
该padding
参数指定两个枚举值之一(不区分大小写):(valid
默认值)或same
。要指定输出张量应该与输入张量具有相同的宽度和高度值,我们padding=same
在这里设置,它指示TensorFlow向输出张量的边缘添加0个值,以保持宽度和高度28.(无填充,a通过28x28张量的5x5卷积将产生24x24张量,因为有24x24个位置从28x28网格中提取5x5瓦。)
该activation
参数指定应用于卷积输出的激活函数。在这里,我们指定ReLU激活 tf.nn.relu
。
我们的输出张力conv2d()
具有与输入相同的宽度和高度尺寸的形状 ,但现在有32个通道保持每个滤镜的输出。[batch_size, 28, 28, 32]
集合层#1
接下来,我们将我们的第一个池层连接到刚创建的卷积层。我们可以使用该max_pooling2d()
方法layers
来构建一个使用2x2过滤器执行最大池的层,并且步长为2:
再次inputs
指定输入张量,形状为 。这里,我们的输入张量是来自第一卷积层的输出,其具有形状。[batch_size,image_width, image_height, channels]
conv1
[batch_size, 28, 28, 32]
注意:与此相反
conv2d()
,
max_pooling2d()
将接受传递参数 的形状。
[channels, batch_size, image_width,image_height]
data_format=channels_first
该pool_size
参数指定了最大池过滤器的大小 (这里)。如果两个维度的值都相同,则可以指定一个整数(例如, )。[width, height]
[2, 2]
pool_size=2
该strides
参数指定步幅的大小。在这里,我们设置一个步长2,这表明由滤波器提取的子区域应在宽度和高度维度上分开2个像素(对于2x2滤镜,这意味着所提取的区域都不会重叠)。如果要为宽度和高度设置不同的步幅值,可以指定一个元组或列表(例如,stride=[3,6]
)。
我们的max_pooling2d()
(pool1
)产生的输出张量具有以下形状 :2x2过滤器将宽度和高度降低了50%。[batch_size, 14, 14, 1]
卷积层#2和Pooling Layer#2
我们可以使用conv2d()
和max_pooling2d()
之前一样将第二个卷积和汇集层连接到我们的CNN 。对于卷积层#2,我们使用ReLU激活配置64个5x5滤波器,对于池#2,我们使用与池化层1相同的规格(2 2×2最大池过滤器,步幅为2):
注意,卷积层#2将第一个合并层(pool1
)的输出张量作为输入,并产生张量h_conv2
作为输出。conv2
具有与(由于)相同的宽度和高度的形状,并且应用64个滤波器的64个通道。[batch_size, 14, 14, 64]
pool1
padding="same"
汇集层#2 conv2
作为输出,产生pool2
输出。pool2
具有形状(宽度和高度减少50%)。[batch_size, 7, 7, 64]
conv2
密集层
接下来,我们要为我们的CNN添加一个密集层(有1,024个神经元和ReLU激活),以对卷积/池层提取的特征进行分类。然而,在连接图层之前,我们将对我们的特征图(pool2
)进行平坦化,使其张量只有两个维度:
在reshape()
上述操作中,-1
该表示batch_size
尺寸将被动态地计算根据在我们的输入数据样本的数目。每个例子都有7(pool2
宽)* 7(pool2
高)* 64(pool2
通道)功能,所以我们希望features
维度的值为7 * 7 * 64(总共3136)。输出张量pool2_flat
具有形状 。[batch_size, 3136]
现在我们可以使用这种dense()
方法layers
连接我们的密集层,如下所示:
该inputs
参数指定输入张:我们的扁平化特征图, pool2_flat
。该units
参数指定在致密层(1024)神经元的数目。该activation
参数可使用的激活函数; 再次,我们将tf.nn.relu
用来添加ReLU激活。
为了帮助改进我们的模型的结果,我们也应用了缺陷正则化到我们的密集层,使用下面的dropout
方法layers
:
再次,inputs
指定输入张量,它是我们的密层(dense
)的输出张量。
该rate
参数指定辍学率; 在这里,我们使用0.4
,这意味着40%的元素将在训练期间随机退出。
该training
参数使用一个布尔值,指定模型当前是否正在训练模式下运行; 如果将只执行差 training
是True
。在这里,我们检查mode
传递给我们的模型函数 cnn_model_fn
是否是TRAIN
模式。
我们的输出张量dropout
具有形状。[batch_size, 1024]
逻辑层
神经网络中的最后一层是逻辑层,它将返回我们预测的原始值。我们创建一个具有10个神经元的密集层(每个目标类别为0-9的一个),具有线性激活(默认):
CNN的最终输出张量logits
有所变化 。[batch_size, 10]
计算损失
对于训练和评估,我们需要定义一个 损失函数 来衡量模型的预测与目标类的匹配程度。对于像MNIST这样的多类分类问题,通常将 交叉熵用作损失度量。以下代码在模型以任一模式运行TRAIN
或EVAL
模式运行时计算交叉熵:
我们来仔细看看上面发生了什么。
我们的labels
张量包含了我们的例子的预测列表,例如[1, 9, ...]
。为了计算交叉熵,首先我们需要转换labels
为相应的 单热编码:
我们使用该tf.one_hot
功能来执行此转换。tf.one_hot()
有两个必需的参数:
indices
。在一个热张力中将具有“on值”的位置,即1
上述张量中的值的位置。
depth
。一个热张量的深度,即目标类的数量。在这里,深度是10
。
以下代码为我们的标签创建单张热张量onehot_labels
:
因为labels
包含从0-9的一系列值,indices
只是我们的 labels
张量,值被转换为整数。这depth
是10
因为我们有10个可能的目标类,每个数字一个。
接下来,我们onehot_labels
从我们的逻辑层计算预测的交叉熵和softmax。tf.losses.softmax_cross_entropy()
采取 onehot_labels
和logits
作为参数,执行softmax激活 logits
,计算交叉熵,并将我们loss
作为标量返回Tensor
:
配置训练操作
在上一节中,我们将CNN的损失定义为逻辑层和我们的标签的softmax交叉熵。让我们配置我们的模型,以便在训练过程中使用tf.contrib.layers.optimize_loss
方法来优化这个损失值 tf.contrib.layers
。我们将使用0.001的学习率和 随机梯度下降 作为优化算法:
注意:有关对Estimator模型函数配置训练操作的更深入的了解,请参阅 “在 tf.contrib.learn中创建估计”教程中的 “ 定义模型的训练操作 ”。
产生预测
我们的模型的逻辑层将我们的预测作为原始值在一 维张量中返回。让我们将这些原始值转换成我们的模型函数可以返回的两种不同格式:[batch_size, 10]
- 每个例子的预测类:从0-9的数字。
- 的概率为每个实施例的每个可能的目标类:该示例是0的概率,是1,是2等
对于一个给定的例子,我们预测的类是具有最高原始值的对数张量的相应行中的元素。我们可以使用以下tf.argmax
函数找到该元素的索引:
该input
参数指定提取最大值的张量 - 这里logits
。该axis
参数指定的轴input
张量沿找到最大的价值。在这里,我们想要找到索引为1的维度上的最大值,这对应于我们的预测(回想我们的对数张量具有形状)。[batch_size, 10]
我们可以通过使用softmax激活从我们的对数层导出概率tf.nn.softmax
:
注意:我们使用
name
参数来明确地命名此操作
softmax_tensor
,所以我们稍后可以引用它。(我们将在 “设置日志钩”中为softmax值 设置日志记录。
我们在一个dict中编译我们的预测如下:
最后,现在我们已经得到了我们predictions
,loss
和train_op
我们能回报他们,与我们一起mode
的说法,在一个tf.contrib.learn.ModelFnOps
对象:
我们编码了我们的MNIST CNN模型函数; 现在我们准备好训练和评估了。
首先,我们加载我们的培训和测试数据。使用以下代码添加一个main()
函数 cnn_mnist.py
:
我们存储和训练标记(从0-9的相应值对每个图像)作为训练特征数据(的手绘数字55000个图像的原始像素值)numpy的阵列 中train_data
和train_labels
分别。同样,我们的evalulation特征数据(10,000张)和评价的标签存储eval_data
和eval_labels
分别。
接下来,我们Estimator
为我们的模型创建一个(TensorFlow类来执行高级模型训练,评估和推断)。将以下代码添加到main()
:
该model_fn
参数指定用于训练,评估和推断的模型函数; 我们通过cnn_model_fn
我们在 “建立CNN MNIST分类器”中创建的。该 model_dir
参数指定将保存模型数据(检查点)的目录(这里,我们指定临时目录/tmp/mnist_convnet_model
,但随意更改为您选择的另一个目录)。
注意:有关TensorFlow
Estimator
API 的深入了解,请参阅 “在tf.contrib.learn中创建估计器”教程 。
由于CNN可能需要一段时间才能进行培训,我们设置一些日志记录,以便跟踪培训过程中的进度。我们可以使用TensorFlow tf.train.SessionRunHook
来创建一个 tf.train.LoggingTensorHook
将记录CNN的softmax层的概率值。将以下内容添加到main()
:
我们存储我们要登录的张量的字母tensors_to_log
。每个键是我们选择的标签,将打印在日志输出中,相应的标签是Tensor
TensorFlow图中的a的名称。在这里,我们 probabilities
可以找到softmax_tensor
,当我们生成概率时,我们早期提供了我们的softmax操作的名字cnn_model_fn
。
注意:如果您没有通过
name
参数明确指定操作的名称,TensorFlow将分配一个默认名称。几个简单的方法来发现应用于操作的名称是在TensorBoard上 可视化图形 )或启用 TensorFlow Debugger(tfdbg)。
接下来,我们创建LoggingTensorHook
,传递tensors_to_log
给 tensors
参数。我们设定every_n_iter=50
,其中规定了每50个训练步骤后记录概率。
现在,我们已经准备好训练我们的模型,我们可以通过调用做fit()
的 mnist_classifier
。将以下内容添加到main()
:
在fit
通话中,我们的训练特征数据和标签传递到x
和y
分别。我们设置batch_size
的100
(这意味着该模型将在每个步骤上的100个例子minibatches培养),和steps
的20000
(这意味着该模型将20,000步骤的总训练)。我们通过我们logging_hook
的monitors
论证,以便在培训期间触发。
培训完成后,我们要评估我们的模型,以确定其在MNIST测试集上的准确性。要设置我们的模型的精度指标,我们需要创建一个计量tf.contrib.learn.MetricSpec
精度的指标。将以下内容添加到main()
:
我们创建了我们MetricSpec
的以下两个参数:
metric_fn
。计算和返回度量值的函数。在这里,我们可以使用模块中的预定义accuracy
功能 tf.metrics
。prediction_key
。包含模型函数返回的预测的张量的关键。在这里,因为我们正在建立分类模型"classes"
,所以我们在“生成预测”中指定了预测键。 现在我们已经设置了我们的metrics
dict,我们可以评估模型。添加以下代码,执行评估并打印结果:
我们通过我们的评估特征数据和标签来evaluate()
在x
和 y
分别论据。这个metrics
论点取决于我们刚刚定义的指标。
我们编写了CNN模型函数Estimator
,以及训练/评估逻辑; 现在我们来看看结果。运行cnn_mnist.py
。
注意:训练CNN是相当计算密集型的。预计完成时间cnn_mnist.py
将根据您的处理器而有所不同,但CPU可能会长达1小时。要更快地训练,您可以减少steps
传递的数量fit()
,但请注意,这将影响精度。
作为模型列车,您将看到如下所示的日志输出:
在这里,我们的测试数据集达到了97.3%的准确度。
要了解有关TensorFlow中TensorFlow估计器和CNN的更多信息,请参阅以下资源: