为什么翻转图像会改变 CNN 池化输出

Why do flipping images change CNN pooling output

我正在研究图像嵌入,想知道为什么翻转图像会改变输出。考虑移除头部的 resnet18 例如:

import torch
import torch.nn as nn
import torchvision.models as models
device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")

model = models.resnet18(pretrained=True)
model.fc = nn.Identity()
model = model.to(device)
model.eval()

x = torch.randn(20, 3, 128, 128).to(device)
with torch.no_grad():
    y1 = model(x)
    y2 = model(x.flip(-1))
    y3 = model(x.flip(-2))

最后一层看起来像这样,最重要的是有一个 AdaptiveAveragePooling 作为最后一层 ,其中像素/特征汇集到 1 个像素

根据我的想法,由于我们只是在卷积之上进行卷积,因此在池化之前,将发生的只是特征图将根据图像的翻转方式进行翻转。平均池只是对最后一个特征图(沿每个通道)进行平均,并且对其方向不变。 AdaptiveMaxPool应该是一样的。

'normal' convnets 之间的主要区别在于我们正在汇集/平均到一个像素宽度。

但是,当我查看 y1-y2y1-y3y2-y3 时,这些值与零有很大不同。我哪里想错了?

我认为池化输出发生了变化,因为池化层的输入没有像我们预期的那样传递。

简答: 输入被翻转但 Conv2d 层的权重没有翻转。这些内核权重也需要根据输入翻转进行翻转以获得预期的输出。

长答案: 这里,根据模型的尾部,Conv2d 的输出传递给 AdaptiveAveragePooling。为了便于理解,我们暂时忽略 BatchNorm

为简单起见,假设输入张量为 x = [1, 3, 5, 4, 7],核为 k =[0.3, 0.5, 0.8]。当它翻转输入时,位置 [0,0] 的输出将是 [0.3*1+0.5*3+0.8*5] = 6.8 并且 [0,2] 将[0.3*5+0.5*4+0.8*7]=9.3 考虑到 stride=1.

现在如果输入翻转,x_flip = [7, 4, 5, 3, 1],位置 [0,0] 的输出将是 [0.3*7+0.5*4+0.8*5] = 8.1 和 [0,2] 将是 [0.3*5+0.5*3+0.8*1] = 3.8.

由于两种情况下输出的头部和尾部不同(8.1 != 9.36.8 != 3.8),我们在卷积层之后得到的输出会有所不同,将 different/unexpected 结果作为池化后的最终输出。

因此,要在此处获得所需的输出,您还需要翻转内核。