TensorFlow 中的 CNN 图像分类(附步骤和示例)
什么是卷积神经网络?
卷积神经网络,也称为卷积网络或 CNN,是计算机视觉应用中一种众所周知的方法。它是一类用于分析视觉图像的深度神经网络。这种类型的架构主要用于从图片或视频中识别对象。它用于图像或视频识别、神经语言处理等应用。
Archi卷积神经网络的结构
想想几年前的 Facebook,当你将照片上传到个人资料后,系统会要求你手动为照片上的脸部添加姓名。如今,Facebook 使用 convnet 自动在照片中标记你的朋友。
用于图像分类的卷积神经网络并不难理解。输入图像在卷积阶段进行处理,然后被赋予标签。
下图可以概括典型的卷积网络架构。首先,将图像推送到网络;这称为输入图像。然后,输入图像经过无数个步骤;这是网络的卷积部分。最后,神经网络可以预测图像上的数字。
图像由具有高度和宽度的像素数组组成。灰度图像只有一个通道,而彩色图像有三个通道(每个通道分别用于红色、绿色和蓝色)。通道相互堆叠。在本教程中,您将使用只有一个通道的灰度图像。每个像素的值从 0 到 255,以反映颜色的强度。例如,等于 0 的像素将显示白色,而值接近 255 的像素将更暗。
让我们看一下存储在 MNIST 数据集下图展示了如何以矩阵形式表示左图。注意,原始矩阵已被标准化为 0 到 1 之间。对于较暗的颜色,矩阵中的值约为 0.9,而白色像素的值为 0。
卷积运算
模型中最关键的部分是卷积层,这部分的目的是为了减小图像的尺寸,以便更快地计算权重,并提高其泛化能力。
在卷积部分,网络保留图像的基本特征并排除不相关的噪声。例如,该模型正在学习如何从背景为山脉的图片中识别大象。如果使用传统的神经网络,模型将为所有像素分配权重,包括那些不重要的、可能误导网络的山脉像素。
相反, Keras 卷积神经网络将使用一种数学技术来提取最相关的像素。这种数学运算称为卷积。这种技术允许网络在每一层学习越来越复杂的特征。卷积将矩阵分成小块,以学习每块中最基本的元素。
卷积神经网络(ConvNet 或 CNN)的组成部分
卷积网络有四个组成部分
- 卷积
- 非线性(ReLU)
- Pooling 或子采样
- 分类(全连接层)
卷积
卷积的目的是局部提取图像上物体的特征。这意味着网络将学习图片中的特定模式,并能够在图片的任何地方识别它。
卷积是一种逐元素乘法。这个概念很容易理解。计算机会扫描图像的一部分,通常尺寸为 3×3,并将其乘以一个过滤器。逐元素乘法的输出称为特征图。重复此步骤,直到扫描完所有图像。注意,卷积后,图像的大小会缩小。
下面有一个 URL,可以实际查看卷积的工作原理。
有许多可用的渠道。下面,我们列出了一些渠道。您可以看到每个过滤器都有特定的用途。请注意,在下图中,内核是过滤器的同义词。
卷积背后的算法
卷积阶段将把滤波器应用到图片中的一小组像素上。滤波器将沿着输入图像移动,一般形状为 3×3 或 5×5。这意味着网络将在整个输入图像上滑动这些窗口并计算卷积。下图显示了卷积的运行方式。补丁的大小为 3×3,输出矩阵是图像矩阵和滤波器之间逐元素运算的结果。
您会注意到输出的宽度和高度可能与输入的宽度和高度不同。这是由于边框效果而发生的。
边界效应
图片有 5×5 的特征图和 3×3 的过滤器。中间只有一个窗口,过滤器可以筛选 3×3 的网格。输出的特征图将缩小两个图块,同时尺寸为 3×3。
为了获得与输入维度相同的输出维度,您需要添加填充。填充包括在矩阵的每一侧添加正确数量的行和列。它将允许卷积以中心拟合每个输入图块。在下图中,输入/输出矩阵具有相同的维度 5×5
当你定义网络时,卷积特征由三个参数控制:
- 深度:它定义了卷积期间要应用的过滤器数量。在上一个示例中,您看到的深度为 1,这意味着只使用一个过滤器。在大多数情况下,有多个过滤器。下图显示了在有三个过滤器的情况下执行的操作
- 迈:它定义两个切片之间的“像素跳跃”数量。如果步幅等于 1,则窗口将以一个像素的跨度移动。如果步幅等于 2,则窗口将跳跃 XNUMX 个像素。如果增加步幅,则特征图会变小。
步幅 1 示例
大步2
- 零填充:填充是在输入特征图的两边添加相应数量的行和列的操作。在这种情况下,输出与输入具有相同的维度。
非线性(ReLU)
在卷积操作结束时,输出将受到激活函数的影响以允许非线性。convnet 的常用激活函数是 Relu。所有具有负值的像素都将被零替换。
Pooling OperaTION
这一步很容易理解。池化的目的是降低输入图像的维数。这些步骤是为了降低操作的计算复杂度。通过降低维数,网络需要计算的权重更少,因此可以防止过度拟合。
在此阶段,您需要定义大小和步幅。池化输入图像的标准方法是使用特征图的最大值。看下面的图片。“池化”将筛选 4×4 特征图的四个子矩阵并返回最大值。池化取 2×2 数组的最大值,然后将此窗口移动两个像素。例如,第一个子矩阵是 [3,1,3,2],池化将返回最大值,即 3。
还有另一种池化操作,例如均值。
此操作会大幅减小特征图的大小
全连接层
最后一步是建立一个传统的 人工神经网络 就像您在上一个教程中所做的那样。您将上一层的所有神经元连接到下一层。您使用 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,您需要定义:
- 卷积层:将 n 个过滤器应用于特征图。卷积后,需要使用 Relu 激活函数为网络添加非线性。
- Pooling 层:卷积之后的下一步是对特征最大值进行下采样。目的是降低特征图的维数,防止过拟合并提高计算速度。最大池化是常规技术,它将特征图划分为子区域(通常为 2×2 大小)并仅保留最大值。
- 全连接层:前几层的所有神经元都连接到下一层。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)