作者: Aritra Roy Gosthipaty
创建日期: 2021/07/25
最后修改: 2021/07/25
描述: 深入探讨位置特定且通道无关的“反卷积”核。
卷积一直是现代计算机视觉神经网络的基础。卷积核是空间无关且通道特定的。因此,它无法针对不同空间位置适应不同的视觉模式。除了与位置相关的问题外,卷积的感受野在捕获长距离空间交互时也带来了挑战。
为了解决上述问题,Li 等人重新思考了卷积的属性 反卷积:反转卷积的内在特性以进行视觉识别。 作者提出了"反卷积核",它是位置特定的且通道无关的。由于操作的空间特定性,作者表示自注意力属于反卷积的设计范式。
本示例描述反卷积核,比较两个图像分类模型,一个使用卷积,另一个使用反卷积,并试图与自注意力层进行对比。
import os
os.environ["KERAS_BACKEND"] = "tensorflow"
import tensorflow as tf
import keras
import matplotlib.pyplot as plt
# 设置种子以保证可重现性。
tf.random.set_seed(42)
卷积仍然是深度神经网络在计算机视觉中的重要组成部分。 要理解反卷积,有必要讨论卷积操作。
考虑一个输入张量 X,其维度为 H、W 和 C_in。我们取一组 C_out 的卷积核,每个核的形状为 K、K、C_in。通过输入张量与卷积核之间的乘加运算,我们得到一个输出张量 Y,其维度为 H、W、C_out。
在上面的图中 C_out=3
。这使得输出张量的形状为 H、
W 和 3。可以注意到,卷积核不依赖于输入张量的空间位置,这使得它是
位置无关的。另一方面,输出张量中的每个通道是基于特定的卷积滤波器,这使它是
通道特定的。
这个想法是有一个操作,既是 位置特定的 又是 通道无关的。试图实现这些特定属性会带来挑战。对于每个 空间位置,如果卷积核的数量是固定的,我们将 无法 处理可变分辨率的输入张量。
为了解决这个问题,作者考虑为特定空间位置条件生成每个卷积核。通过这种方法,我们应该能够轻松处理可变分辨率的输入张量。下面的图提供了关于这种卷积核生成方法的直观理解。
class Involution(keras.layers.Layer):
def __init__(
self, channel, group_number, kernel_size, stride, reduction_ratio, name
):
super().__init__(name=name)
# 初始化参数。
self.channel = channel
self.group_number = group_number
self.kernel_size = kernel_size
self.stride = stride
self.reduction_ratio = reduction_ratio
def build(self, input_shape):
# 获取输入的形状。
(_, height, width, num_channels) = input_shape
# 根据步幅缩放高度和宽度。
height = height // self.stride
width = width // self.stride
# 定义一个层,该层对输入张量进行平均池化
# 如果步幅大于1。
self.stride_layer = (
keras.layers.AveragePooling2D(
pool_size=self.stride, strides=self.stride, padding="same"
)
if self.stride > 1
else tf.identity
)
# 定义核生成层。
self.kernel_gen = keras.Sequential(
[
keras.layers.Conv2D(
filters=self.channel // self.reduction_ratio, kernel_size=1
),
keras.layers.BatchNormalization(),
keras.layers.ReLU(),
keras.layers.Conv2D(
filters=self.kernel_size * self.kernel_size * self.group_number,
kernel_size=1,
),
]
)
# 定义重塑层
self.kernel_reshape = keras.layers.Reshape(
target_shape=(
height,
width,
self.kernel_size * self.kernel_size,
1,
self.group_number,
)
)
self.input_patches_reshape = keras.layers.Reshape(
target_shape=(
height,
width,
self.kernel_size * self.kernel_size,
num_channels // self.group_number,
self.group_number,
)
)
self.output_reshape = keras.layers.Reshape(
target_shape=(height, width, num_channels)
)
def call(self, x):
# 根据输入张量生成核。
# B, H, W, K*K*G
kernel_input = self.stride_layer(x)
kernel = self.kernel_gen(kernel_input)
# 重塑核
# B, H, W, K*K, 1, G
kernel = self.kernel_reshape(kernel)
# 提取输入补丁。
# B, H, W, K*K*C
input_patches = tf.image.extract_patches(
images=x,
sizes=[1, self.kernel_size, self.kernel_size, 1],
strides=[1, self.stride, self.stride, 1],
rates=[1, 1, 1, 1],
padding="SAME",
)
# 重塑输入补丁以与后续操作对齐。
# B, H, W, K*K, C//G, G
input_patches = self.input_patches_reshape(input_patches)
# 计算核和补丁的乘加操作。
# B, H, W, K*K, C//G, G
output = tf.multiply(kernel, input_patches)
# B, H, W, C//G, G
output = tf.reduce_sum(output, axis=3)
# 重塑输出核。
# B, H, W, C
output = self.output_reshape(output)
# 返回输出张量和核。
return output, kernel
# 定义输入张量。
input_tensor = tf.random.normal((32, 256, 256, 3))
# 计算步幅为1的反卷积。
output_tensor, _ = Involution(
channel=3, group_number=1, kernel_size=5, stride=1, reduction_ratio=1, name="inv_1"
)(input_tensor)
print(f"步幅为1的输出形状: {output_tensor.shape}")
# 计算步幅为2的反卷积。
output_tensor, _ = Involution(
channel=3, group_number=1, kernel_size=5, stride=2, reduction_ratio=1, name="inv_2"
)(input_tensor)
print(f"步幅为2的输出形状: {output_tensor.shape}")
# 计算步幅为1,通道数为16,降维比为2的反卷积。
output_tensor, _ = Involution(
channel=16, group_number=1, kernel_size=5, stride=1, reduction_ratio=2, name="inv_3"
)(input_tensor)
print(
"通道数为16,降维比为2的输出形状: {}".format(output_tensor.shape)
)
步幅为1的输出形状: (32, 256, 256, 3)
步幅为2的输出形状: (32, 128, 128, 3)
通道数为16,降维比为2的输出形状: (32, 256, 256, 3)
在本节中,我们将构建一个图像分类器模型。将有两个模型,一个使用卷积,另一个使用反卷积。
该图像分类模型深受谷歌卷积神经网络 (CNN)教程的启发。
# 加载CIFAR10数据集。
print("加载CIFAR10数据集...")
(
(train_images, train_labels),
(
test_images,
test_labels,
),
) = keras.datasets.cifar10.load_data()
# 将像素值标准化到0和1之间。
(train_images, test_images) = (train_images / 255.0, test_images / 255.0)
# 打乱并分批数据集。
train_ds = (
tf.data.Dataset.from_tensor_slices((train_images, train_labels))
.shuffle(256)
.batch(256)
)
test_ds = tf.data.Dataset.from_tensor_slices((test_images, test_labels)).batch(256)
加载CIFAR10数据集...
class_names = [
"飞机",
"汽车",
"鸟",
"猫",
"鹿",
"狗",
"青蛙",
"马",
"船",
"卡车",
]
plt.figure(figsize=(10, 10))
for i in range(25):
plt.subplot(5, 5, i + 1)
plt.xticks([])
plt.yticks([])
plt.grid(False)
plt.imshow(train_images[i])
plt.xlabel(class_names[train_labels[i][0]])
plt.show()
# 构建卷积模型。
print("构建卷积模型...")
conv_model = keras.Sequential(
[
keras.layers.Conv2D(32, (3, 3), input_shape=(32, 32, 3), padding="same"),
keras.layers.ReLU(name="relu1"),
keras.layers.MaxPooling2D((2, 2)),
keras.layers.Conv2D(64, (3, 3), padding="same"),
keras.layers.ReLU(name="relu2"),
keras.layers.MaxPooling2D((2, 2)),
keras.layers.Conv2D(64, (3, 3), padding="same"),
keras.layers.ReLU(name="relu3"),
keras.layers.Flatten(),
keras.layers.Dense(64, activation="relu"),
keras.layers.Dense(10),
]
)
# 使用必要的损失函数和优化器编译模型。
print("编译卷积模型...")
conv_model.compile(
optimizer="adam",
loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=["accuracy"],
)
# 训练模型。
print("卷积模型训练中...")
conv_hist = conv_model.fit(train_ds, epochs=20, validation_data=test_ds)
构建卷积模型...
编译卷积模型...
卷积模型训练...
第 1/20 轮
196/196 ━━━━━━━━━━━━━━━━━━━━ 6s 15ms/step - 准确率: 0.3068 - 损失: 1.9000 - 验证准确率: 0.4861 - 验证损失: 1.4593
第 2/20 轮
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 4ms/step - 准确率: 0.5153 - 损失: 1.3603 - 验证准确率: 0.5741 - 验证损失: 1.1913
第 3/20 轮
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 5ms/step - 准确率: 0.5949 - 损失: 1.1517 - 验证准确率: 0.6095 - 验证损失: 1.0965
第 4/20 轮
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 5ms/step - 准确率: 0.6414 - 损失: 1.0330 - 验证准确率: 0.6260 - 验证损失: 1.0635
第 5/20 轮
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 5ms/step - 准确率: 0.6690 - 损失: 0.9485 - 验证准确率: 0.6622 - 验证损失: 0.9833
第 6/20 轮
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 5ms/step - 准确率: 0.6951 - 损失: 0.8764 - 验证准确率: 0.6783 - 验证损失: 0.9413
第 7/20 轮
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 5ms/step - 准确率: 0.7122 - 损失: 0.8167 - 验证准确率: 0.6856 - 验证损失: 0.9134
第 8/20 轮
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 4ms/step - 准确率: 0.7299 - 损失: 0.7709 - 验证准确率: 0.7001 - 验证损失: 0.8792
第 9/20 轮
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 4ms/step - 准确率: 0.7467 - 损失: 0.7288 - 验证准确率: 0.6992 - 验证损失: 0.8821
第 10/20 轮
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 4ms/step - 准确率: 0.7591 - 损失: 0.6982 - 验证准确率: 0.7235 - 验证损失: 0.8237
第 11/20 轮
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 4ms/step - 准确率: 0.7725 - 损失: 0.6550 - 验证准确率: 0.7115 - 验证损失: 0.8521
第 12/20 轮
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 5ms/step - 准确率: 0.7808 - 损失: 0.6302 - 验证准确率: 0.7051 - 验证损失: 0.8823
第 13/20 轮
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 5ms/step - 准确率: 0.7860 - 损失: 0.6101 - 验证准确率: 0.7122 - 验证损失: 0.8635
第 14/20 轮
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 5ms/step - 准确率: 0.7998 - 损失: 0.5786 - 验证准确率: 0.7214 - 验证损失: 0.8348
第 15/20 轮
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 5ms/step - 准确率: 0.8117 - 损失: 0.5473 - 验证准确率: 0.7139 - 验证损失: 0.8835
第 16/20 轮
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 5ms/step - 准确率: 0.8168 - 损失: 0.5267 - 验证准确率: 0.7155 - 验证损失: 0.8840
第 17/20 轮
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 5ms/step - 准确率: 0.8266 - 损失: 0.5022 - 验证准确率: 0.7239 - 验证损失: 0.8576
第 18/20 轮
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 5ms/step - 准确率: 0.8374 - 损失: 0.4750 - 验证准确率: 0.7262 - 验证损失: 0.8756
第 19/20 轮
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 5ms/step - 准确率: 0.8452 - 损失: 0.4505 - 验证准确率: 0.7235 - 验证损失: 0.9049
第 20/20 轮
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 4ms/step - 准确率: 0.8531 - 损失: 0.4283 - 验证准确率: 0.7304 - 验证损失: 0.8962
# 构建反卷积模型。
print("building the involution model...")
inputs = keras.Input(shape=(32, 32, 3))
x, _ = Involution(
channel=3, group_number=1, kernel_size=3, stride=1, reduction_ratio=2, name="inv_1"
)(inputs)
x = keras.layers.ReLU()(x)
x = keras.layers.MaxPooling2D((2, 2))(x)
x, _ = Involution(
channel=3, group_number=1, kernel_size=3, stride=1, reduction_ratio=2, name="inv_2"
)(x)
x = keras.layers.ReLU()(x)
x = keras.layers.MaxPooling2D((2, 2))(x)
x, _ = Involution(
channel=3, group_number=1, kernel_size=3, stride=1, reduction_ratio=2, name="inv_3"
)(x)
x = keras.layers.ReLU()(x)
x = keras.layers.Flatten()(x)
x = keras.layers.Dense(64, activation="relu")(x)
outputs = keras.layers.Dense(10)(x)
inv_model = keras.Model(inputs=[inputs], outputs=[outputs], name="inv_model")
# 使用必要的损失函数和优化器编译模型。
print("compiling the involution model...")
inv_model.compile(
optimizer="adam",
loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=["accuracy"],
)
# 训练模型
print("inv model training...")
inv_hist = inv_model.fit(train_ds, epochs=20, validation_data=test_ds)
building the involution model...
compiling the involution model...
inv model training...
Epoch 1/20
196/196 ━━━━━━━━━━━━━━━━━━━━ 9s 25ms/step - accuracy: 0.1369 - loss: 2.2728 - val_accuracy: 0.2716 - val_loss: 2.1041
Epoch 2/20
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 5ms/step - accuracy: 0.2922 - loss: 1.9489 - val_accuracy: 0.3478 - val_loss: 1.8275
Epoch 3/20
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 5ms/step - accuracy: 0.3477 - loss: 1.8098 - val_accuracy: 0.3782 - val_loss: 1.7435
Epoch 4/20
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 6ms/step - accuracy: 0.3741 - loss: 1.7420 - val_accuracy: 0.3901 - val_loss: 1.6943
Epoch 5/20
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 5ms/step - accuracy: 0.3931 - loss: 1.6942 - val_accuracy: 0.4007 - val_loss: 1.6639
Epoch 6/20
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 5ms/step - accuracy: 0.4057 - loss: 1.6622 - val_accuracy: 0.4108 - val_loss: 1.6494
Epoch 7/20
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 6ms/step - accuracy: 0.4134 - loss: 1.6374 - val_accuracy: 0.4202 - val_loss: 1.6363
Epoch 8/20
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 6ms/step - accuracy: 0.4200 - loss: 1.6166 - val_accuracy: 0.4312 - val_loss: 1.6062
Epoch 9/20
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 5ms/step - accuracy: 0.4286 - loss: 1.5949 - val_accuracy: 0.4316 - val_loss: 1.6018
Epoch 10/20
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 5ms/step - accuracy: 0.4346 - loss: 1.5794 - val_accuracy: 0.4346 - val_loss: 1.5963
Epoch 11/20
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 6ms/step - accuracy: 0.4395 - loss: 1.5641 - val_accuracy: 0.4388 - val_loss: 1.5831
Epoch 12/20
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 5ms/step - accuracy: 0.4445 - loss: 1.5502 - val_accuracy: 0.4443 - val_loss: 1.5826
Epoch 13/20
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 6ms/step - accuracy: 0.4493 - loss: 1.5391 - val_accuracy: 0.4497 - val_loss: 1.5574
Epoch 14/20
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 6ms/step - accuracy: 0.4528 - loss: 1.5255 - val_accuracy: 0.4547 - val_loss: 1.5433
Epoch 15/20
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 4ms/step - accuracy: 0.4575 - loss: 1.5148 - val_accuracy: 0.4548 - val_loss: 1.5438
Epoch 16/20
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 6ms/step - accuracy: 0.4599 - loss: 1.5072 - val_accuracy: 0.4581 - val_loss: 1.5323
Epoch 17/20
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 6ms/step - accuracy: 0.4664 - loss: 1.4957 - val_accuracy: 0.4598 - val_loss: 1.5321
Epoch 18/20
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 6ms/step - accuracy: 0.4701 - loss: 1.4863 - val_accuracy: 0.4575 - val_loss: 1.5302
Epoch 19/20
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 6ms/step - accuracy: 0.4737 - loss: 1.4790 - val_accuracy: 0.4676 - val_loss: 1.5233
Epoch 20/20
196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 6ms/step - accuracy: 0.4771 - loss: 1.4740 - val_accuracy: 0.4719 - val_loss: 1.5096
在本节中,我们将查看这两种模型并比较几个要点。
可以看到,在类似架构下,CNN的参数数量远大于INN(反卷积神经网络)的参数数量。
conv_model.summary()
inv_model.summary()
模型: "sequential_3"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┓ ┃ 层 (类型) ┃ 输出形状 ┃ 参数 # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━┩ │ conv2d_6 (卷积层) │ (None, 32, 32, 32) │ 896 │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ relu1 (ReLU) │ (None, 32, 32, 32) │ 0 │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ max_pooling2d (最大池化层) │ (None, 16, 16, 32) │ 0 │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ conv2d_7 (卷积层) │ (None, 16, 16, 64) │ 18,496 │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ relu2 (ReLU) │ (None, 16, 16, 64) │ 0 │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ max_pooling2d_1 (最大池化层) │ (None, 8, 8, 64) │ 0 │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ conv2d_8 (卷积层) │ (None, 8, 8, 64) │ 36,928 │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ relu3 (ReLU) │ (None, 8, 8, 64) │ 0 │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ flatten (扁平化层) │ (None, 4096) │ 0 │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ dense (Dense) │ (无, 64) │ 262,208 │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ dense_1 (Dense) │ (无, 10) │ 650 │ └─────────────────────────────────┴───────────────────────────┴────────────┘
总参数: 957,536 (3.65 MB)
可训练参数: 319,178 (1.22 MB)
不可训练参数: 0 (0.00 B)
优化器参数: 638,358 (2.44 MB)
模型: "inv_model"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┓ ┃ 层 (类型) ┃ 输出形状 ┃ 参数 # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━┩ │ input_layer_4 (InputLayer) │ (无, 32, 32, 3) │ 0 │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ inv_1 (Involution) │ [(无, 32, 32, 3), │ 26 │ │ │ (无, 32, 32, 9, 1, 1)] │ │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ re_lu_4 (ReLU) │ (无, 32, 32, 3) │ 0 │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ max_pooling2d_2 (MaxPooling2D) │ (None, 16, 16, 3) │ 0 │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ inv_2 (Involution) │ [(None, 16, 16, 3), │ 26 │ │ │ (None, 16, 16, 9, 1, 1)] │ │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ re_lu_6 (ReLU) │ (None, 16, 16, 3) │ 0 │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ max_pooling2d_3 (MaxPooling2D) │ (None, 8, 8, 3) │ 0 │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ inv_3 (Involution) │ [(None, 8, 8, 3), (None, │ 26 │ │ │ 8, 8, 9, 1, 1)] │ │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ re_lu_8 (ReLU) │ (None, 8, 8, 3) │ 0 │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ flatten_1 (Flatten) │ (None, 192) │ 0 │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ dense_2 (Dense) │ (None, 64) │ 12,352 │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ dense_3 (Dense) │ (None, 10) │ 650 │ └─────────────────────────────────┴───────────────────────────┴────────────┘
总参数: 39,230 (153.25 KB)
可训练参数: 13,074 (51.07 KB)
不可训练参数: 6 (24.00 B)
优化器参数: 26,150 (102.15 KB)
这里,损失和准确率图展示了INNs是缓慢的学习者(参数较少)。
plt.figure(figsize=(20, 5))
plt.subplot(1, 2, 1)
plt.title("卷积损失")
plt.plot(conv_hist.history["loss"], label="损失")
plt.plot(conv_hist.history["val_loss"], label="验证损失")
plt.legend()
plt.subplot(1, 2, 2)
plt.title("反卷积损失")
plt.plot(inv_hist.history["loss"], label="损失")
plt.plot(inv_hist.history["val_loss"], label="验证损失")
plt.legend()
plt.show()
plt.figure(figsize=(20, 5))
plt.subplot(1, 2, 1)
plt.title("卷积准确率")
plt.plot(conv_hist.history["accuracy"], label="准确率")
plt.plot(conv_hist.history["val_accuracy"], label="验证准确率")
plt.legend()
plt.subplot(1, 2, 2)
plt.title("反卷积准确率")
plt.plot(inv_hist.history["accuracy"], label="准确率")
plt.plot(inv_hist.history["val_accuracy"], label="验证准确率")
plt.legend()
plt.show()
为了可视化核,我们将每个反卷积核的K×K值求和。不同空间位置的所有代表框架对应的热图。
作者提到:
“我们提出的反卷积令人想起自我注意,并本质上可以成为它的广义版本。”
通过核的可视化,我们确实可以得到图像的注意力图。学习的反卷积核为输入张量的各个空间位置提供了关注。位置特定的特性使反卷积成为一个通用的模型空间,自我注意属于其中。
layer_names = ["inv_1", "inv_2", "inv_3"]
outputs = [inv_model.get_layer(name).output[1] for name in layer_names]
vis_model = keras.Model(inv_model.input, outputs)
fig, axes = plt.subplots(nrows=10, ncols=4, figsize=(10, 30))
for ax, test_image in zip(axes, test_images[:10]):
(inv1_kernel, inv2_kernel, inv3_kernel) = vis_model.predict(test_image[None, ...])
inv1_kernel = tf.reduce_sum(inv1_kernel, axis=[-1, -2, -3])
inv2_kernel = tf.reduce_sum(inv2_kernel, axis=[-1, -2, -3])
inv3_kernel = tf.reduce_sum(inv3_kernel, axis=[-1, -2, -3])
ax[0].imshow(keras.utils.array_to_img(test_image))
ax[0].set_title("输入图像")
ax[1].imshow(keras.utils.array_to_img(inv1_kernel[0, ..., None]))
ax[1].set_title("反卷积核 1")
ax[2].imshow(keras.utils.array_to_img(inv2_kernel[0, ..., None]))
ax[2].set_title("反卷积核 2")
ax[3].imshow(keras.utils.array_to_img(inv3_kernel[0, ..., None]))
ax[3].set_title("反卷积核 3")
1/1 ━━━━━━━━━━━━━━━━━━━━ 1s 503ms/步
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/步
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/步
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/步
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/步
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/步
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/步
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/步
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/步
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/步
在这个例子中,主要的重点是构建一个可以轻松重用的Involution
层。尽管我们的比较是基于特定任务的,但欢迎你将该层用于不同的任务并报告你的结果。
在我看来,involution的关键要点是它与自注意力的关系。与位置特定和通道特定处理的直觉在许多任务中是合理的。
向前看,人们可以:
你可以使用托管在Hugging Face Hub上的训练模型,并在Hugging Face Spaces上尝试演示。