为什么翻转图像会改变 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-y2
、y1-y3
、y2-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.3 和 6.8 != 3.8),我们在卷积层之后得到的输出会有所不同,将 different/unexpected 结果作为池化后的最终输出。
因此,要在此处获得所需的输出,您还需要翻转内核。
我正在研究图像嵌入,想知道为什么翻转图像会改变输出。考虑移除头部的 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-y2
、y1-y3
、y2-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.3 和 6.8 != 3.8),我们在卷积层之后得到的输出会有所不同,将 different/unexpected 结果作为池化后的最终输出。
因此,要在此处获得所需的输出,您还需要翻转内核。