使用视觉变换器进行图像分类的实践指南

148 阅读7分钟

视觉变换器是深度学习领域中流行的变换器之一。在视觉变换器出现之前,我们不得不在计算机视觉中使用卷积神经网络来完成复杂的任务。随着视觉变换器的引入,我们得到了一个更强大的计算机视觉任务模型,就像我们有BERTGPT 用于复杂的NLP 任务一样。在这篇文章中,我们将学习如何将视觉变换器用于图像分类任务。为此,我们将展示一个用于图像分类的视觉变换器的实践实现。以下是本文要介绍的主要内容。

目录

  1. 关于视觉变换器
  2. 实现用于图像分类的视觉变换器

第1步:初始化设置

第2步:建立网络

第3步 构建视觉变换器

第4步:编译和训练

让我们先来了解一下视觉变换器。

关于视觉变压器

视觉转化器(ViT)是计算机视觉领域中使用的转化器,其工作原理是基于自然语言处理领域中使用的转化器的工作性质。在内部,转化器通过测量输入标记对之间的关系进行学习。在计算机视觉中,我们可以使用图像的斑块作为标记。这种关系可以通过在网络中提供注意力来学习。这可以与卷积网络结合起来,或者通过替换卷积网络的一些组件来完成。这些结构的网络可以应用于图像分类任务。使用视觉变换器进行图像分类的全部程序可以通过以下图像来解释。

图像来源

在上面的图片中,我们可以看到我们所需要遵循的程序。在这篇文章中,我们将讨论如何使用Keras库执行所有这些步骤。

对于这个实现,我们将采取以下步骤。

第1步:初始化设置

在这一部分,我们将执行建模的一些基本程序,如导入数据集、定义超参数、数据增强等。

步骤1.1:导入数据

让我们从获取数据开始。在这个过程中,我们将使用由Keras库提供的CIFAR-10数据集。在该数据集中,我们有50,000张大小为32×32的图像在训练数据集中,10,000张同样大小的图像在测试数据集中。我们对这些图像有以下标签。

索引标签描述
10飛機
21汽车
32鸟类
43
54鹿
65
76青蛙
87
98
109卡车

我们可以用以下几行代码调用这个数据集:

from keras.datasets import cifar10
(x_train, y_train), (x_test, y_test) = cifar10.load_data()

输出:

检查数据集的形状:

print(f"x_train: {x_train.shape} - y_train: {y_train.shape}")
print(f"x_test: {x_test.shape} - y_test: {y_test.shape}")

输出:

步骤1.2:定义超参数

在这一节中,我们将定义一些参数,我们将与其他子过程一起使用。

learning_rate = 0.001
weight_decay = 0.0001
batch_size = 256
num_epochs = 100
image_size = 72
patch_size = 6  
num_patches = (image_size // patch_size) ** 2
projection_dim = 64
num_heads = 4
transformer_units = [projection_dim * 2,projection_dim,] 
transformer_layers = 8
mlp_head_units = [2048, 1024]

考虑到上述参数,我们可以说,在这个过程中,我们将在训练中使用100个epochs,并将调整图像的大小,将图像转换成补丁。

现在,我们将调用重要的库。

步骤1.3:数据增强

在这个过程中,我们将向转换器提供增强的图像。在增强过程中,我们将对图像进行标准化和调整大小,然后我们将随机翻转图像。这个过程将以连续的方法和使用Keras提供的层来完成:

import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
import tensorflow_addons as tfa
data_augmentation = keras.Sequential(
    [
        layers.Normalization(),
        layers.Resizing(72, 72),
        layers.RandomFlip("horizontal"),
        layers.RandomRotation(factor=0.02),
        layers.RandomZoom(
            height_factor=0.2, width_factor=0.2
        ),
    ],
    name="data_augmentation",
)
data_augmentation.layers[0].adapt(x_train)

在增强的最后一步,我们将计算训练数据的平均值和方差,以便进行归一化。

步骤1.4 图像的可视化

让我们看看数据集中的图像会是什么样子:

import matplotlib.pyplot as plt
 
plt.figure(figsize=(4, 4))
image = x_train[np.random.choice(range(x_train.shape[0]))]
plt.imshow(image.astype("uint8"))
plt.axis("off")

输出:

上面的输出是数据集中的一个图像的例子,由于数据中的图像尺寸较小,所以不能清楚地看到。现在我们可以进行第二步了。

第2步:构建网络

在这一步,我们将建立一个网络,我们将使用一个MLP网络和一个将我们的图像分成补丁的层。此外,我们将使用一个补丁编码器来转换补丁,它将把补丁投射到64大小的向量中。让我们从建立一个MLP网络开始。

步骤2.1:构建MLP网络

def mlp(x, hidden_units, dropout_rate):
    for units in hidden_units:
        x = layers.Dense(units, activation=tf.nn.gelu)(x)
        x = layers.Dropout(dropout_rate)(x)
    return x

在上面的代码中,我们可以看到我们已经建立了一个MLP网络,它只是有一个密集层和一个放弃层。

步骤2.2 补丁制作者

在这一步,我们将定义一个可以将图像转换成补丁的网络。为此,我们主要使用张量流提供的extract_patches模块。

class Patches(layers.Layer):
    def __init__(self, patch_size):
        super(Patches, self).__init__()
        self.patch_size = patch_size
 
    def call(self, images):
        batch_size = tf.shape(images)[0]
        patches = tf.image.extract_patches(
            images=images,
            sizes=[1, self.patch_size, self.patch_size, 1],
            strides=[1, self.patch_size, self.patch_size, 1],
            rates=[1, 1, 1, 1],
            padding="VALID",
        )
        patch_dims = patches.shape[-1]
        patches = tf.reshape(patches, [batch_size, -1, patch_dims])
        return patches
n = int(np.sqrt(patches.shape[1]))
plt.figure(figsize=(4, 4))
for i, patch in enumerate(patches[0]):
    ax = plt.subplot(n, n, i + 1)
    patch_img = tf.reshape(patch, (patch_size, patch_size, 3))
    plt.imshow(patch_img.numpy().astype("uint8"))
    plt.axis("off")

输出:

在上面的输出中,我们可以看到,我们已经将图像转换成了补丁,视觉转换器将利用这些补丁来学习对图像进行分类。

步骤2.3:补丁编码器

这个补丁编码器将对图像补丁进行线性转换,并在投影向量中添加可学习的位置嵌入:

class PatchEncoder(layers.Layer):
    def __init__(self, num_patches, projection_dim):
        super(PatchEncoder, self).__init__()
        self.num_patches = num_patches
        self.projection = layers.Dense(units=projection_dim)
        self.position_embedding = layers.Embedding(
            input_dim=num_patches, output_dim=projection_dim
        )
 
    def call(self, patch):
        positions = tf.range(start=0, limit=self.num_patches, delta=1)
        encoded = self.projection(patch) + self.position_embedding(positions)
        return encoded

在建立这个网络之后,我们就可以建立一个视觉变换器模型了。

第3步:建立视觉变换器

在这一节中,我们将为视觉变换器建立模块。正如上面讨论和实现的那样,我们将使用增强的数据,这些数据将通过补丁制作器块,然后数据将通过补丁编码器块。在变换器块中,我们将在补丁序列上使用一个自我注意层。转化器块的输出将通过一个分类头,这将有助于产生最终的输出。让我们在下面的代码中看看:

def create_vit_classifier():
    inputs = layers.Input(shape=input_shape)
    augmented = data_augmentation(inputs)
    patches = Patches(patch_size)(augmented)
    encoded_patches = PatchEncoder(num_patches, projection_dim)(patches)
 
    for _ in range(transformer_layers):
        x1 = layers.LayerNormalization(epsilon=1e-6)(encoded_patches)
        attention_output = layers.MultiHeadAttention(
            num_heads=num_heads, key_dim=projection_dim, dropout=0.1
        )(x1, x1)
        x2 = layers.Add()([attention_output, encoded_patches])
        x3 = layers.LayerNormalization(epsilon=1e-6)(x2)
        x3 = mlp(x3, hidden_units=transformer_units, dropout_rate=0.1)
        encoded_patches = layers.Add()([x3, x2])
 
    representation = layers.LayerNormalization(epsilon=1e-6)(encoded_patches)
    representation = layers.Flatten()(representation)
    representation = layers.Dropout(0.5)(representation)
    features = mlp(representation, hidden_units=mlp_head_units, dropout_rate=0.5)
    logits = layers.Dense(num_classes)(features)
    model = keras.Model(inputs=inputs, outputs=logits)
    return model

使用上述函数,我们可以使用视觉转化器定义一个分类器,在其中我们提供了数据增强、补丁制作和补丁编码的方法。编码后的补丁将成为我们最终的输入,作为转化器的图像表示。扁平化层将帮助我们改变输出的形状。

第四步:编译和训练

在这一节中,我们将编译和训练我们所创建的模型,之后,我们将评估该模型的准确性。

步骤4.1:编译模型

使用下面这行代码,我们可以编译模型:

optimizer = tfa.optimizers.AdamW(learning_rate=learning_rate, weight_decay=weight_decay)
 
model.compile(
    optimizer=optimizer,
    loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),
    metrics=[
       keras.metrics.SparseCategoricalAccuracy(name="accuracy"),
       keras.metrics.SparseTopKCategoricalAccuracy(5, name="top-5-accuracy"), ],)

在编译过程中,我们使用了亚当优化器和稀疏分类交叉熵损失。

步骤4.2:训练

使用以下几行代码可以完成转化器的训练:

history = model.fit(
    x=x_train,
    y=y_train,
    batch_size=batch_size,
    epochs=num_epochs,
    validation_split=0.1,)

输出:

在上面的输出中,我们可以看到,训练已经开始。它可能需要大量的时间。所以为了快速完成,建议在训练过程中启用GPU。在谷歌Colab中,我们可以在运行时标签下的管理运行时会话标签中找到GPU的设置。

步骤4.3:检查准确性

让我们在图像分类任务中检查视觉变换器的准确性。

_, accuracy, top_5_accuracy = model.evaluate(x_test, y_test)
print(f"Test accuracy: {round(accuracy * 100, 2)}%")
print(f"Test top 5 accuracy: {round(top_5_accuracy * 100, 2)}%")

输出:

在上面的输出中,我们可以看到我们的模型有84.21%的准确率的表现,我们的前5名准确率是99.24%。

最后的话

在这篇文章中,我们已经介绍了变换器,并看到它是如何通过图像用于图像分类的。我们使用CIFAR-10数据集实现了一个用于图像分类的视觉变换器,并遵循图像中包含的所有步骤。 我们使用这个变换器在任务中取得了非常好的结果。

参考文献