视觉变换器是深度学习领域中流行的变换器之一。在视觉变换器出现之前,我们不得不在计算机视觉中使用卷积神经网络来完成复杂的任务。随着视觉变换器的引入,我们得到了一个更强大的计算机视觉任务模型,就像我们有BERT和GPT 用于复杂的NLP 任务一样。在这篇文章中,我们将学习如何将视觉变换器用于图像分类任务。为此,我们将展示一个用于图像分类的视觉变换器的实践实现。以下是本文要介绍的主要内容。
目录
- 关于视觉变换器
- 实现用于图像分类的视觉变换器
第1步:初始化设置
第2步:建立网络
第3步 构建视觉变换器
第4步:编译和训练
让我们先来了解一下视觉变换器。
关于视觉变压器
视觉转化器(ViT)是计算机视觉领域中使用的转化器,其工作原理是基于自然语言处理领域中使用的转化器的工作性质。在内部,转化器通过测量输入标记对之间的关系进行学习。在计算机视觉中,我们可以使用图像的斑块作为标记。这种关系可以通过在网络中提供注意力来学习。这可以与卷积网络结合起来,或者通过替换卷积网络的一些组件来完成。这些结构的网络可以应用于图像分类任务。使用视觉变换器进行图像分类的全部程序可以通过以下图像来解释。

在上面的图片中,我们可以看到我们所需要遵循的程序。在这篇文章中,我们将讨论如何使用Keras库执行所有这些步骤。
对于这个实现,我们将采取以下步骤。
第1步:初始化设置
在这一部分,我们将执行建模的一些基本程序,如导入数据集、定义超参数、数据增强等。
步骤1.1:导入数据
让我们从获取数据开始。在这个过程中,我们将使用由Keras库提供的CIFAR-10数据集。在该数据集中,我们有50,000张大小为32×32的图像在训练数据集中,10,000张同样大小的图像在测试数据集中。我们对这些图像有以下标签。
| 索引 | 标签 | 描述 |
| 1 | 0 | 飛機 |
| 2 | 1 | 汽车 |
| 3 | 2 | 鸟类 |
| 4 | 3 | 猫 |
| 5 | 4 | 鹿 |
| 6 | 5 | 狗 |
| 7 | 6 | 青蛙 |
| 8 | 7 | 马 |
| 9 | 8 | 船 |
| 10 | 9 | 卡车 |
我们可以用以下几行代码调用这个数据集:
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数据集实现了一个用于图像分类的视觉变换器,并遵循图像中包含的所有步骤。 我们使用这个变换器在任务中取得了非常好的结果。