Ниже мы представляем сеть функциональных пирамид. Цитируется ниже [1]
-
Рисунок (а) представляет собой довольно распространенный многомасштабный метод, называемыйfeaturized image pyramid, этот метод широко использовался в более ранних функциях, разработанных вручную (DPM), а также в CNN. Он предназначен для выполнения мультимасштабирования на входном изображении, что достигается установкой различных коэффициентов масштабирования. Это может решить многомасштабность, но это эквивалентно обучению нескольких моделей (при условии, что размер входных данных должен быть фиксированным), даже если размер входных данных может быть переменным, это также увеличивает объем памяти для хранения изображений различных напольные весы.
-
Рисунок (b) представляет собой CNN. По сравнению с искусственно созданными функциями, CNN может самостоятельно изучать более продвинутые семантические функции. В то же время CNN устойчива к масштабным изменениям. Поэтому, как показано на рисунке, функции, рассчитанные на основе входных данных единая шкала также может использоваться для идентификации, но при обнаружении очевидного многомасштабного обнаружения цели пирамидальная структура по-прежнему необходима для дальнейшего повышения точности.
Судя по некоторым ведущим методам в наборах данных imageNet и COCO, в тесте используется метод пирамиды изображений с признаками, то есть комбинация (а), (б). Преимущество характеристики каждого уровня пирамиды изображений заключается в том, что генерируются многомасштабные представления признаков, а признаки каждого уровня имеют сильную семантику (поскольку все они генерируются CNN), включая уровень высокого разрешения (входное изображение наибольший масштаб).
Однако этот режим имеет очевидные недостатки: по сравнению с исходным методом время увеличивается в 4 раза, что сложно использовать в приложениях реального времени, а также увеличивается стоимость хранения, из-за чего пирамида изображений используется только в тестовая фаза. Но если он используется только на этапе тестирования, то обучение и тестирование будут несовместимы при выводе. Поэтому некоторые современные методы просто отказываются от пирамиды изображений.
Но пирамиды изображений — не единственный способ вычисления мультимасштабных представлений объектов. deepCNN способен к иерархическим функциям, и из-за объединения он будет генерировать пирамидальные функции с присущей им многомасштабностью. Но проблема в том, что карта высокого разрешения (мелкий слой) имеет низкоуровневые признаки, поэтому производительность распознавания целей мелкого слоя слабая. Это также является целью слияния разных уровней.
-
Как показано на (c), SSD ранее пытался использовать иерархические функции CNN в форме пирамиды. В идеале пирамиды в стиле SSD повторно используют многомасштабные карты объектов из нескольких слоев, вычисленных в прямом процессе, поэтому эта форма не потребляет дополнительных ресурсов. ноЧтобы избежать использования низкоуровневых функций, SSD отказался от мелкой карты функций, но начал строить пирамиды из conv4_3 и добавил несколько новых слоев.. Поэтому SSD отказывается от повторного использования карт объектов с более высоким разрешением, но этоЭти карты признаков очень важны для обнаружения небольших объектов. В этом разница между SSD и FPN.
-
Рисунок (4) представляет собой структуру FPN, FPN дляЕстественно использовать пирамидальную форму функций уровня CNN, создавая пирамиды функций с надежной семантической информацией во всех масштабах.. Так устроена структура FPNСтруктура сверху вниз и боковые соединения для объединения мелких слоев с высоким разрешением и глубоких слоев с богатой семантической информацией.Вот и всеИз одного входного изображения в одном масштабе быстро создавайте пирамиды признаков с надежной семантической информацией во всех масштабах без значительных затрат.
Давайте снова посмотрим на аналогичную сеть:
Вышеупомянутая структура сети с пропущенным соединением выполняется на самом тонком уровне (последний слой сверху вниз) во время прогнозирования, что простоПосле многократного повышения частоты дискретизации и объединения функций с последним шагом функции, сгенерированные на последнем этапе, используются для прогнозирования.Структура сети FPN аналогична вышеописанной, разница в том, чтоПрогнозирование выполняется независимо в каждом слое. Более поздние эксперименты доказывают, что самый точный уровень не так хорош, как FPN, потому что сеть FPN представляет собой детектор скользящего окна с фиксированным размером окна, поэтому скольжение на разных уровнях пирамиды может повысить ее устойчивость к изменениям масштаба. Кроме того, хотя самый лучший уровень имеет больше привязок, он все же не так хорош, как FPN, что указывает на то, что увеличение количества привязок не может эффективно повысить точность.
восходящий путь
Упреждающий расчет CNN - это восходящий путь. Карта объектов рассчитывается ядром свертки, которое обычно становится все меньше и меньше. Существуют также некоторые слои объектов, выходные данные которых совпадают с исходным размером, который называется «тот же самый». сетевой этап" (тот же сетевой этап). этап ). Для пирамиды признаков в этой статье авторы определяют уровень пирамиды для каждого этапа, а затем выбирают выходные данные последнего слоя каждого этапа в качестве эталонного набора карт признаков. Такой выбор естественен, так как самый глубокий слой каждой стадии должен иметь самые сильные черты. В частности, для ResNets авторы используют выходные данные активации функции последней остаточной структуры каждого этапа. Обозначим эти остаточные выходы модуля как {C2, C3, C4, C5}, соответствующие выходам conv2, conv3, conv4 и conv5, и обратите внимание, что они имеют шаги {4, 8, 16, 32} пикселей относительно входа изображение длинное. Учитывая объем памяти, conv1 не включен в пирамиду.
Пути сверху вниз и боковые связи
путь сверху вниз (the top-down pathway) как совместить низкоуровневые фичи с высоким разрешением? Путь в том,Повышение дискретизации более абстрактной и семантически более сильной высокоуровневой карты объектов, а затем боковое подключение объекта к объекту предыдущего слоя, чтобы улучшить высокоуровневый объект.. Стоит отметить, что особенности двух слоев боковых связей должны быть одинаковыми по пространственному размеру. Это должно быть сделано в первую очередь для того, чтобы воспользоваться низкоуровневыми деталями позиционирования.
На следующем рисунке показаны детали подключения. Удвойте передискретизацию функций высокого уровня (метод повышения частоты ближайшего соседа, вы можете обратиться к деконволюции), а затем объедините его с соответствующими функциями предыдущего слоя (предыдущий слой можно использовать только после ядра свертки 1 * 1, цель Это изменение каналов, которые должны совпадать с каналами следующего слоя), а метод комбинирования заключается в добавлении между пикселями. Этот процесс повторяется итеративно до тех пор, пока не будет создана лучшая карта признаков. В начале итерации автор добавил ядро свертки 1 * 1 за слоем C5, чтобы создать самую грубую карту признаков, и, наконец, автор использовал ядро свертки 3 * 3 для обработки объединенной карты признаков (чтобы исключить повышающая дискретизация) эффекты сглаживания) для создания окончательной желаемой карты объектов.Чтобы иметь возможность совместно использовать слой классификации на всех уровнях в более позднем приложении, выходной канал после свертки 3 * 3 фиксируется здесь как d, который здесь установлен на 256. Следовательно, все дополнительные сверточные слои (такие как P2) имеют 256-канальный выход. Эти дополнительные слои не используют нелинейность.
Соответствующими слоями объектов слияния слоев {C2, C3, C4, C5} являются {P2, P3, P4, P5}, и соответствующие размеры пространства слоев одинаковы.
1 диаграмма
2 код
See the paper "Feature Pyramid Networks for Object Detection" for more details.
import torch.nn.functional as F
from torch.autograd import Variable
class Bottleneck(nn.Module):
def __init__(self, in_planes, planes, stride=1):
super(Bottleneck, self).__init__()
self.conv1 = nn.Conv2d(in_planes, planes, kernel_size=1, bias=False)
self.bn1 = nn.BatchNorm2d(planes)
self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride, padding=1, bias=False)
self.bn2 = nn.BatchNorm2d(planes)
self.conv3 = nn.Conv2d(planes, self.expansion*planes, kernel_size=1, bias=False)
self.bn3 = nn.BatchNorm2d(self.expansion*planes)
self.shortcut = nn.Sequential()
if stride != 1 or in_planes != self.expansion*planes:
self.shortcut = nn.Sequential(
nn.Conv2d(in_planes, self.expansion*planes, kernel_size=1, stride=stride, bias=False),
nn.BatchNorm2d(self.expansion*planes)
out = F.relu(self.bn1(self.conv1(x)))
out = F.relu(self.bn2(self.conv2(out)))
out = self.bn3(self.conv3(out))
def __init__(self, block, num_blocks):
super(FPN, self).__init__()
self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False)
self.bn1 = nn.BatchNorm2d(64)
self.layer1 = self._make_layer(block, 64, num_blocks[0], stride=1)
self.layer2 = self._make_layer(block, 128, num_blocks[1], stride=2)
self.layer3 = self._make_layer(block, 256, num_blocks[2], stride=2)
self.layer4 = self._make_layer(block, 512, num_blocks[3], stride=2)
self.toplayer = nn.Conv2d(2048, 256, kernel_size=1, stride=1, padding=0)
self.smooth1 = nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1)
self.smooth2 = nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1)
self.smooth3 = nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1)
self.latlayer1 = nn.Conv2d(1024, 256, kernel_size=1, stride=1, padding=0)
self.latlayer2 = nn.Conv2d( 512, 256, kernel_size=1, stride=1, padding=0)
self.latlayer3 = nn.Conv2d( 256, 256, kernel_size=1, stride=1, padding=0)
def _make_layer(self, block, planes, num_blocks, stride):
strides = [stride] + [1]*(num_blocks-1)
layers.append(block(self.in_planes, planes, stride))
self.in_planes = planes * block.expansion
return nn.Sequential(*layers)
def _upsample_add(self, x, y):
'''Upsample and add two feature maps.
x: (Variable) top feature map to be upsampled.
y: (Variable) lateral feature map.
(Variable) added feature map.
Note in PyTorch, when input size is odd, the upsampled feature map
with `F.upsample(..., scale_factor=2, mode='nearest')`
maybe not equal to the lateral feature map size.
original input size: [N,_,15,15] ->
conv2d feature map size: [N,_,8,8] ->
upsampled feature map size: [N,_,16,16]
So we choose bilinear upsample which supports arbitrary output sizes.
return F.upsample(x, size=(H,W), mode='bilinear') + y
c1 = F.relu(self.bn1(self.conv1(x)))
c1 = F.max_pool2d(c1, kernel_size=3, stride=2, padding=1)
p4 = self._upsample_add(p5, self.latlayer1(c4))
print(f'latlayer1(c4):{self.latlayer1(c4).shape}, p4:{p4.shape}')
p3 = self._upsample_add(p4, self.latlayer2(c3))
print(f'latlayer1(c3):{self.latlayer2(c3).shape}, p3:{p3.shape}')
p2 = self._upsample_add(p3, self.latlayer3(c2))
print(f'latlayer1(c2):{self.latlayer3(c2).shape}, p2:{p2.shape}')
return FPN(Bottleneck, [2,2,2,2])
fms = net(Variable(torch.randn(1,3,600,900)))
вывод:
c1:torch.Size([1, 64, 150, 225])
c2:torch.Size([1, 256, 150, 225])
c3:torch.Size([1, 512, 75, 113])
c4:torch.Size([1, 1024, 38, 57])
c5:torch.Size([1, 2048, 19, 29])
p5:torch.Size([1, 256, 19, 29])
latlayer1(c4):torch.Size([1, 256, 38, 57]), p4:torch.Size([1, 256, 38, 57])
latlayer1(c3):torch.Size([1, 256, 75, 113]), p3:torch.Size([1, 256, 75, 113])
latlayer1(c2):torch.Size([1, 256, 150, 225]), p2:torch.Size([1, 256, 150, 225])
torch.Size([1, 256, 150, 225])
torch.Size([1, 256, 75, 113])
torch.Size([1, 256, 38, 57])
torch.Size([1, 256, 19, 29])