Почему перелистывание изображений меняет вывод пула CNN

Я смотрю на вложения изображений и задаюсь вопросом, почему переворачивание изображений меняет вывод. Например, рассмотрим 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 должно было быть то же самое.

Ключевое различие между «обычными» консетями заключается в том, что мы объединяем/усредняем до ширины в один пиксель.

Однако, когда я смотрю на y1-y2, y1-y3, y2-y3, значения значительно отличаются от нуля. О чем я неправильно думаю?


person sachinruk    schedule 09.10.2020    source источник


Ответы (1)


Я думаю, что выходные данные пула изменены, потому что входные данные для слоя пула не передаются, как мы ожидали.

Короткий ответ: переворачиваются входные данные, но не веса слоев 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), результат, который мы получаем после слоя свертки, будет другим, что дает разные/неожиданные результаты в качестве конечного результата после объединения.

Итак, чтобы получить здесь желаемый результат, вам также нужно перевернуть ядро.

person Arth Patel    schedule 12.10.2020