TensorFlow 中的 CNN 图像分类(附步骤和示例)

什么是卷积神经网络?

卷积神经网络,也称为卷积网络或 CNN,是计算机视觉应用中一种众所周知的方法。它是一类用于分析视觉图像的深度神经网络。这种类型的架构主要用于从图片或视频中识别对象。它用于图像或视频识别、神经语言处理等应用。

Archi卷积神经网络的结构

想想几年前的 Facebook,当你将照片上传到个人资料后,系统会要求你手动为照片上的脸部添加姓名。如今,Facebook 使用 convnet 自动在照片中标记你的朋友。

用于图像分类的卷积神经网络并不难理解。输入图像在卷积阶段进行处理,然后被赋予标签。

下图可以概括典型的卷积网络架构。首先,将图像推送到网络;这称为输入图像。然后,输入图像经过无数个步骤;这是网络的卷积部分。最后,神经网络可以预测图像上的数字。

Archi卷积神经网络(CNN)的结构
Archi卷积神经网络(CNN)的结构

图像由具有高度和宽度的像素数组组成。灰度图像只有一个通道,而彩色图像有三个通道(每个通道分别用于红色、绿色和蓝色)。通道相互堆叠。在本教程中,您将使用只有一个通道的灰度图像。每个像素的值从 0 到 255,以反映颜色的强度。例如,等于 0 的像素将显示白色,而值接近 255 的像素将更暗。

让我们看一下存储在 MNIST 数据集下图展示了如何以矩阵形式表示左图。注意,原始矩阵已被标准化为 0 到 1 之间。对于较暗的颜色,矩阵中的值约为 0.9,而白色像素的值为 0。

卷积神经网络

卷积运算

模型中最关键的部分是卷积层,这部分的目的是为了减小图像的尺寸,以便更快地计算权重,并提高其泛化能力。

在卷积部分,网络保留图像的基本特征并排除不相关的噪声。例如,该模型正在学习如何从背景为山脉的图片中识别大象。如果使用传统的神经网络,模型将为所有像素分配权重,包括那些不重要的、可能误导网络的山脉像素。

相反, Keras 卷积神经网络将使用一种数学技术来提取最相关的像素。这种数学运算称为卷积。这种技术允许网络在每一层学习越来越复杂的特征。卷积将矩阵分成小块,以学习每块中最基本的元素。

卷积神经网络(ConvNet 或 CNN)的组成部分

卷积网络有四个组成部分

  1. 卷积
  2. 非线性(ReLU)
  3. Pooling 或子采样
  4. 分类(全连接层)

卷积

卷积的目的是局部提取图像上物体的特征。这意味着网络将学习图片中的特定模式,并能够在图片的任何地方识别它。

卷积是一种逐元素乘法。这个概念很容易理解。计算机会扫描图像的一部分,通常尺寸为 3×3,并将其乘以一个过滤器。逐元素乘法的输出称为特征图。重复此步骤,直到扫描完所有图像。注意,卷积后,图像的大小会缩小。

卷积

下面有一个 URL,可以实际查看卷积的工作原理。

卷积

有许多可用的渠道。下面,我们列出了一些渠道。您可以看到每个过滤器都有特定的用途。请注意,在下图中,内核是过滤器的同义词。

卷积

卷积背后的算法

卷积阶段将把滤波器应用到图片中的一小组像素上。滤波器将沿着输入图像移动,一般形状为 3×3 或 5×5。这意味着网络将在整个输入图像上滑动这些窗口并计算卷积。下图显示了卷积的运行方式。补丁的大小为 3×3,输出矩阵是图像矩阵和滤波器之间逐元素运算的结果。

卷积背后的算法

您会注意到输出的宽度和高度可能与输入的宽度和高度不同。这是由于边框效果而发生的。

边界效应

图片有 5×5 的特征图和 3×3 的过滤器。中间只有一个窗口,过滤器可以筛选 3×3 的网格。输出的特征图将缩小两个图块,同时尺寸为 3×3。

边界效应

为了获得与输入维度相同的输出维度,您需要添加填充。填充包括在矩阵的每一侧添加正确数量的行和列。它将允许卷积以中心拟合每个输入图块。在下图中,输入/输出矩阵具有相同的维度 5×5

边界效应

当你定义网络时,卷积特征由三个参数控制:

  1. 深度:它定义了卷积期间要应用的过滤器数量。在上一个示例中,您看到的深度为 1,这意味着只使用一个过滤器。在大多数情况下,有多个过滤器。下图显示了在有三个过滤器的情况下执行的操作

边界效应

  1. :它定义两个切片之间的“像素跳跃”数量。如果步幅等于 1,则窗口将以一个像素的跨度移动。如果步幅等于 2,则窗口将跳跃 XNUMX 个像素。如果增加步幅,则特征图会变小。

步幅 1 示例

步幅示例

大步2

步幅示例

  1. 零填充:填充是在输入特征图的两边添加相应数量的行和列的操作。在这种情况下,输出与输入具有相同的维度。

非线性(ReLU)

在卷积操作结束时,输出将受到激活函数的影响以允许非线性。convnet 的常用激活函数是 Relu。所有具有负值的像素都将被零替换。

Pooling OperaTION

这一步很容易理解。池化的目的是降低输入图像的维数。这些步骤是为了降低操作的计算复杂度。通过降低维数,网络需要计算的权重更少,因此可以防止过度拟合。

在此阶段,您需要定义大小和步幅。池化输入图像的标准方法是使用特征图的最大值。看下面的图片。“池化”将筛选 4×4 特征图的四个子矩阵并返回最大值。池化取 2×2 数组的最大值,然后将此窗口移动两个像素。例如,第一个子矩阵是 [3,1,3,2],池化将返回最大值,即 3。

Pooling OperaTION

还有另一种池化操作,例如均值。

此操作会大幅减小特征图的大小

全连接层

最后一步是建立一个传统的 人工神经网络 就像您在上一个教程中所做的那样。您将上一层的所有神经元连接到下一层。您使用 softmax 激活函数对输入图像上的数字进行分类。

回顾:

TensorFlow 卷积神经网络在做出预测之前会编译不同的层。神经网络具有:

  • 卷积层
  • Relu 激活函数
  • Poolin层
  • 密集连接层

卷积层对图片的子区域应用不同的过滤器。Relu 激活函数增加了非线性,而池化层降低了特征图的维数。

所有这些层都从图像中提取了必要的信息。最后,特征图被输入到具有 softmax 函数的主要全连接层以进行预测。

使用 TensorFlow 训练 CNN

现在你已经熟悉了卷积神经网络的构建模块,你可以使用 TensorFlow.我们将使用 MNIST 数据集进行 CNN 图像分类。

数据准备与上一个教程相同。您可以运行代码并直接跳转到 CNN 的架构。

您将按照以下步骤使用 CNN 进行图像分类:

步骤 1:上传数据集

第 2 步:输入层

步骤3:卷积层

步骤4: Poolin层

步骤 5:第二卷积层和 Poolin层

步骤 6:密集层

步骤 7:Logit 层

步骤 1:上传数据集

MNIST 数据集可通过 scikit 进行学习 网址。请下载并存储在下载中。您可以使用 fetch_mldata('MNIST original') 上传它。

创建训练/测试集

您需要使用 train_test_split 拆分数据集

扩展功能

最后,您可以使用 MinMaxScaler 缩放特征,如下面的使用 TensorFlow CNN 示例的图像分类所示。

import numpy as np
import tensorflow as tf
from sklearn.datasets import fetch_mldata

#Change USERNAME by the username of your machine
## Windows USER
mnist = fetch_mldata('C:\\Users\\USERNAME\\Downloads\\MNIST original')
## Mac User
mnist = fetch_mldata('/Users/USERNAME/Downloads/MNIST original')

print(mnist.data.shape)
print(mnist.target.shape)
from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(mnist.data, mnist.target, test_size=0.2, random_state=42)
y_train  = y_train.astype(int)
y_test  = y_test.astype(int)
batch_size =len(X_train)

print(X_train.shape, y_train.shape,y_test.shape )
## resclae
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
# Train
X_train_scaled = scaler.fit_transform(X_train.astype(np.float64))
# test
X_test_scaled = scaler.fit_transform(X_test.astype(np.float64))
feature_columns = [tf.feature_column.numeric_column('x', shape=X_train_scaled.shape[1:])]
X_train_scaled.shape[1:]

定义 CNN

与传统神经网络相比,CNN 使用图像原始像素上的过滤器来学习细节模式,而不是全局模式。要构建 CNN,您需要定义:

  1. 卷积层:将 n 个过滤器应用于特征图。卷积后,需要使用 Relu 激活函数为网络添加非线性。
  2. Pooling 层:卷积之后的下一步是对特征最大值进行下采样。目的是降低特征图的维数,防止过拟合并提高计算速度。最大池化是常规技术,它将特征图划分为子区域(通常为 2×2 大小)并仅保留最大值。
  3. 全连接层:前几层的所有神经元都连接到下一层。CNN 将根据卷积层的特征对标签进行分类,并使用池化层进行缩减。

CNN 架构

  • 卷积层:应用 14 个 5×5 滤波器(提取 5×5 像素子区域),具有 ReLU 激活函数
  • Pooling 层:使用 2×2 过滤器和步幅为 2 执行最大池化(指定池化区域不重叠)
  • 卷积层:应用 36 个 5×5 滤波器,具有 ReLU 激活函数
  • Pooling 第 2 层:同样使用 2×2 过滤器和步长为 2 执行最大池化
  • 1,764 个神经元,dropout 正则化率为 0.4(训练过程中任何给定元素被丢弃的概率为 0.4)
  • 密集层(Logits 层):10 个神经元,每个数字目标类别(0-9)一个。

创建 CNN 有三个重要模块:

  • conv2d().以过滤器数量、过滤器核大小、填充和激活函数作为参数构造一个二维卷积层。
  • max_pooling2d(). 使用最大池化算法构造二维池化层。
  • 密集()。用隐藏层和单元构造一个密集层

您将定义一个函数来构建 CNN。让我们详细了解如何构建每个构建块,然后将所有内容包装到函数中。

第 2 步:输入层

def cnn_model_fn(features, labels, mode):
    input_layer = tf.reshape(tensor = features["x"],shape =[-1, 28, 28, 1])

您需要定义一个具有数据形状的张量。为此,您可以使用模块 tf.reshape。在此模块中,您需要声明要重塑的张量和张量的形状。第一个参数是数据的特征,它在函数的参数中定义。

图片有高度、宽度和通道。MNIST 数据集是一张 28×28 大小的单色图片。我们在形状参数中将批处理大小设置为 -1,以便它采用特征 [“x”] 的形状。这样做的好处是让批处理大小成为可调整的超参数。如果将批处理大小设置为 7,则张量将输入 5,488 个值(28*28*7)。

步骤3:卷积层

# first Convolutional Layer
  conv1 = tf.layers.conv2d(
      inputs=input_layer,
      filters=14,
      kernel_size=[5, 5],
      padding="same",
      activation=tf.nn.relu)

第一个卷积层有 14 个过滤器,内核大小为 5×5,填充相同。填充相同意味着输出张量和输入张量应具有相同的高度和宽度。Tensorflow 会在行和列中添加零以确保大小相同。

使用 Relu 激活函数。输出大小将为 [28, 28, 14]。

步骤4: Poolin层

卷积之后的下一步是池化计算。池化计算将降低数据的维数。您可以使用大小为 2×2、步长为 2 的模块 max_pooling2d。使用上一层作为输入。输出大小将为 [batch_size, 14, 14, 14]

# first Pooling Layer 
pool1 = tf.layers.max_pooling2d(inputs=conv1, pool_size=[2, 2], strides=2)

步骤 5:第二卷积层和 Poolin层

第二个卷积层有 32 个过滤器,输出大小为 [batch_size, 14, 14, 32]。池化层的大小与之前相同,输出形状为 [batch_size, 14, 14, 18]。

conv2 = tf.layers.conv2d(
      inputs=pool1,
      filters=36,
      kernel_size=[5, 5],
      padding="same",
      activation=tf.nn.relu)
pool2 = tf.layers.max_pooling2d(inputs=conv2, pool_size=[2, 2], strides=2)

步骤 6:密集层

然后,你需要定义全连接层。特征图必须先展平,然后才能与密集层连接。你可以使用尺寸为 7*7*36 的模块重塑。

密集层将连接 1764 个神经元。您添加了一个 Relu 激活函数。此外,您还添加了一个速率为 0.3 的 dropout 正则化项,这意味着 30% 的权重将设置为 0。请注意,dropout 仅在训练阶段发生。函数 cnn_model_fn 有一个参数模式,用于声明模型是否需要训练或评估,如下面的 CNN 图像分类 TensorFlow 示例所示。

pool2_flat = tf.reshape(pool2, [-1, 7 * 7 * 36])

dense = tf.layers.dense(inputs=pool2_flat, units=7 * 7 * 36, activation=tf.nn.relu)
dropout = tf.layers.dropout(
      inputs=dense, rate=0.3, training=mode == tf.estimator.ModeKeys.TRAIN)

步骤 7:Logit 层

最后,在 TensorFlow 图像分类示例中,您可以用模型的预测定义最后一层。输出形状等于批次大小和 10(图像总数)。

# Logits Layer
logits = tf.layers.dense(inputs=dropout, units=10)

您可以创建一个包含类别和每个类别的概率的字典。模块 tf.argmax() 返回 logit 层的最高值。softmax 函数返回每个类别的概率。

predictions = {				
	# Generate predictions				
    "classes": tf.argmax(input=logits, axis=1),				
    "probabilities": tf.nn.softmax(logits, name="softmax_tensor")  }			

您只想在模式设置为预测时返回字典预测。您添加此代码来显示预测

if mode == tf.estimator.ModeKeys.PREDICT:
    return tf.estimator.EstimatorSpec(mode=mode, predictions=predictions)

下一步是计算模型的损失。在上一个教程中,您了解到多类模型的损失函数是交叉熵。使用以下代码可以轻松计算损失:

# Calculate Loss (for both TRAIN and EVAL modes)
loss = tf.losses.sparse_softmax_cross_entropy(labels=labels, logits=logits)

TensorFlow CNN 示例的最后一步是优化模型,即找到权重的最佳值。为此,您可以使用学习率为 0.001 的梯度下降优化器。目标是尽量减少损失

optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.001)
train_op = optimizer.minimize(
        loss=loss,
        global_step=tf.train.get_global_step())

您已完成 CNN。但是,您想在评估模式下显示性能指标。多类模型的性能指标是准确度指标。Tensorflow 配备了一个模块准确度,该模块有两个参数,即标签和预测值。

eval_metric_ops = {
      "accuracy": tf.metrics.accuracy(labels=labels, predictions=predictions["classes"])}
return tf.estimator.EstimatorSpec(mode=mode, loss=loss, eval_metric_ops=eval_metric_ops)

就这样。您创建了第一个 CNN,并准备将所有内容包装到一个函数中,以便使用它来训练和评估模型。

def cnn_model_fn(features, labels, mode):
  """Model function for CNN."""
  # Input Layer
  input_layer = tf.reshape(features["x"], [-1, 28, 28, 1])

  # Convolutional Layer
  conv1 = tf.layers.conv2d(
      inputs=input_layer,
      filters=32,
      kernel_size=[5, 5],
      padding="same",
      activation=tf.nn.relu)

  # Pooling Layer
  pool1 = tf.layers.max_pooling2d(inputs=conv1, pool_size=[2, 2], strides=2)

  # Convolutional Layer #2 and Pooling Layer
  conv2 = tf.layers.conv2d(
      inputs=pool1,
      filters=36,
      kernel_size=[5, 5],
      padding="same",
      activation=tf.nn.relu)
  pool2 = tf.layers.max_pooling2d(inputs=conv2, pool_size=[2, 2], strides=2)

  # Dense Layer
  pool2_flat = tf.reshape(pool2, [-1, 7 * 7 * 36])
  dense = tf.layers.dense(inputs=pool2_flat, units=7 * 7 * 36, activation=tf.nn.relu)
  dropout = tf.layers.dropout(
      inputs=dense, rate=0.4, training=mode == tf.estimator.ModeKeys.TRAIN)

  # Logits Layer
  logits = tf.layers.dense(inputs=dropout, units=10)

  predictions = {
      # Generate predictions (for PREDICT and EVAL mode)
      "classes": tf.argmax(input=logits, axis=1),
      "probabilities": tf.nn.softmax(logits, name="softmax_tensor")
  }

  if mode == tf.estimator.ModeKeys.PREDICT:
    return tf.estimator.EstimatorSpec(mode=mode, predictions=predictions)

  # Calculate Loss
  loss = tf.losses.sparse_softmax_cross_entropy(labels=labels, logits=logits)

  # Configure the Training Op (for TRAIN mode)
  if mode == tf.estimator.ModeKeys.TRAIN:
    optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.001)
    train_op = optimizer.minimize(
        loss=loss,
        global_step=tf.train.get_global_step())
    return tf.estimator.EstimatorSpec(mode=mode, loss=loss, train_op=train_op)

  # Add evaluation metrics Evaluation mode
  eval_metric_ops = {
      "accuracy": tf.metrics.accuracy(
          labels=labels, predictions=predictions["classes"])}
  return tf.estimator.EstimatorSpec(
      mode=mode, loss=loss, eval_metric_ops=eval_metric_ops)

以下步骤与之前的教程相同。

首先,使用 CNN 模型定义一个用于图像分类的估计器。

# Create the Estimator
mnist_classifier = tf.estimator.Estimator(
    model_fn=cnn_model_fn, model_dir="train/mnist_convnet_model")

CNN 需要多次训练,因此,您可以创建一个 Logging 挂钩,每 50 次迭代存储一次 softmax 层的值。

# Set up logging for predictions
tensors_to_log = {"probabilities": "softmax_tensor"}
logging_hook = tf.train.LoggingTensorHook(tensors=tensors_to_log, every_n_iter=50)

您已准备好估算模型。您将批处理大小设置为 100 并对数据进行混洗。请注意,我们将训练步骤设置为 16.000,训练可能需要大量时间。请耐心等待。

# Train the model
train_input_fn = tf.estimator.inputs.numpy_input_fn(
    x={"x": X_train_scaled},
    y=y_train,
    batch_size=100,
    num_epochs=None,
    shuffle=True)
mnist_classifier.train(
    input_fn=train_input_fn,
    steps=16000,
    hooks=[logging_hook])

现在模型已经训练完毕,你可以对其进行评估并打印结果

# Evaluate the model and print results
eval_input_fn = tf.estimator.inputs.numpy_input_fn(
    x={"x": X_test_scaled},
    y=y_test,
    num_epochs=1,
    shuffle=False)
eval_results = mnist_classifier.evaluate(input_fn=eval_input_fn)
print(eval_results)
INFO:tensorflow:Calling model_fn.
INFO:tensorflow:Done calling model_fn.
INFO:tensorflow:Starting evaluation at 2018-08-05-12:52:41
INFO:tensorflow:Graph was finalized.
INFO:tensorflow:Restoring parameters from train/mnist_convnet_model/model.ckpt-15652
INFO:tensorflow:Running local_init_op.
INFO:tensorflow:Done running local_init_op.
INFO:tensorflow:Finished evaluation at 2018-08-05-12:52:56
INFO:tensorflow:Saving dict for global step 15652: accuracy = 0.9589286, global_step = 15652, loss = 0.13894269
{'accuracy': 0.9689286, 'loss': 0.13894269, 'global_step': 15652}

使用当前架构,您可以获得 97% 的准确率。您可以更改架构、批量大小和迭代次数以提高准确率。CNN 神经网络的表现远优于 ANN 或逻辑回归。在人工神经网络教程中,您的准确率为 96%,低于 CNN。CNN 在处理较大图像时的表现令人印象深刻,无论是计算速度还是准确性。

总结

卷积神经网络在评估图片方面非常有效。这种类型的架构在从图片或视频中识别对象方面占主导地位。

要构建 TensorFlow CNN,您需要遵循七个步骤:

步骤1:上传数据集:

MNIST 数据集可通过 scikit 进行学习。请下载并存储在下载中。您可以使用 fetch_mldata('MNIST original') 上传它。

步骤2:输入层:

此步骤重塑数据。形状等于像素数的平方根。例如,如果图片有 156 个像素,则形状为 26×26。您需要指定图片是否有颜色。如果是,则形状为 3 - RGB 为 3,否则为 1。

input_layer = tf.reshape(tensor = features["x"],shape =[-1, 28, 28, 1])

步骤3:卷积层

接下来,您需要创建卷积层。您可以应用不同的过滤器,让网络学习重要特征。您可以指定内核的大小和过滤器的数量。

conv1 = tf.layers.conv2d(
      inputs=input_layer,
      filters=14,
      kernel_size=[5, 5],
      padding="same",
      activation=tf.nn.relu)

步骤4: Poolin层

在第三步中,添加池化层。此层会减小输入的大小。它通过取子矩阵的最大值来实现这一点。例如,如果子矩阵为 [3,1,3,2],则池化将返回最大值,即 3。

pool1 = tf.layers.max_pooling2d(inputs=conv1, pool_size=[2, 2], strides=2)

步骤5:添加卷积层和 Poolin层

在此步骤中,您可以添加任意数量的卷积层和池化层。Google 使用的架构包含超过 20 个卷积层。

步骤6:致密层

第 6 步将前一步展开,以创建完全连接的层。在此步骤中,您可以使用不同的激活函数并添加 dropout 效果。

pool2_flat = tf.reshape(pool2, [-1, 7 * 7 * 36])

dense = tf.layers.dense(inputs=pool2_flat, units=7 * 7 * 36, activation=tf.nn.relu)
dropout = tf.layers.dropout(
      inputs=dense, rate=0.3, training=mode == tf.estimator.ModeKeys.TRAIN)

步骤7:Logit 层

最后一步是预测。

logits = tf.layers.dense(inputs=dropout, units=10)