Интерпретация документов по обнаружению контуров | Расширенные сверточные функции для обнаружения краев | CVPR | 2017

искусственный интеллект

0 Обзор

Мне кажется, что это сочинениеЭто улучшение сети HED CVPR 2015 (обнаружение целостных вложенных краев), и документ RCF в основном сравнивается с сетью HED во всем мире..

В прошлой статье мы смутно помним, что модель HED имеет такой график:

Среди них карты характеристик пяти побочных выходов HED.Следующий рисунок - это рисунок в документе RCF:

Мы можем понять улучшение RCF по сравнению с HED по разнице между этими двумя цифрами.Вы можете взглянуть на рисунок.

Раскройте ответ:

  • HED — изображение леопарда, а RCF — изображение двух птиц (ручные собачьи головы)
  • В HED это карта характеристик вывода побочного вывода, а в RCF это conv3_1, conv3_2, что означаетRCF, похоже, использует карту выходных признаков каждой свертки в качестве побочного вывода..

Правильно, HED выбирает 5 побочных выходных данных, каждый боковой выход представляет собой карту объектов, выводимую слоем свертки перед слоем пула; а RCF использует выходную карту объектов каждой свертки в качестве дополнительного вывода, другими словамиВ финальном стороннем выводе может быть более одного вывода одинакового размера..

Если вы еще этого не понимаете, см. следующую главу «Структура модели».

1 Структура модели

Основой RCF является модель VGG:

Как видно из рисунка:

  • Магистральная сеть разделена на состояния от 1 до 5, этап 1 имеет два сверточных слоя, этап 2 имеет два сверточных слоя, всего 13 сверточных слоев, изображение, выводимое каждой сверткой, дополнительно подключено к свертке 1x1. Чтобы уменьшить количество каналов, вы можете видеть, что на рисунке присутствует большое количество 21-канальных сверточных слоев.
  • 21-канальная карта признаков того же этапа склеивается в 42-канальную или 63-канальную карту признаков, затем проходит сверточный слой 1x1, чтобы уменьшить количество каналов до 1, а затем проходит через сигмовидный слой. для вывода Результат является побочным выводом в модели RCF

2 Функция потерь

Функция потерь здесь фактически аналогична HED:

Во-первых, функция потерь по-прежнему использует бинарную кросс-энтропию.

вY|Y^-|представляет отрицательное значение пикселя,Y+|Y^+|Представляет положительное значение пикселя.Вообще говоря, в задаче обнаружения контуров положительных образцов должно быть меньше, поэтомуальфа\alphaЗначение мало, поэтому в первой строке функции потерь y=0, то есть при расчете потерь неконтурной части будет добавлен меньший вес, чтобы избежать проблемы дисбаланса классов.

В функции потерь есть две константы, однаλ\lambda, это постоянная веса, по умолчанию 1,1, другаян\eta. Описание в статье такое:

Наборы данных краев в этом сообществе обычно помечаются несколькими аннотаторами, использующими свои знания о наличии объектов и частей объектов. Хотя люди различаются по познанию, эти помеченные человеком края для одного и того же изображения имеют высокую согласованность. Для каждого изображения мы усредняем все наземная правда для создания карты вероятности края, которая находится в диапазоне от 0 до 1. Здесь 0 означает, что ни один аннотатор не помечен в этом пикселе, а 1 означает, что все аннотаторы пометили в этом пикселе.Мы рассматриваем пиксели с вероятностью края выше η как положительные выборки и пиксели с вероятностью края, равной 0, как отрицательные выборки. В противном случае, если пиксель отмечен менее чем η аннотаторов, этот пиксель может быть семантически спорным, чтобы быть точкой края. Таким образом, рассматривать ли его как положительный или отрицательный выборки могут сбить сеть с толку, поэтому мы игнорируем пиксели в этой категории.

Общая идея такова: как правило, маркировка набора данных выполняется несколькими людьми. Хотя у разных людей разное сознание, их контурные аннотации к одному и тому же изображению часто совпадают. Окончательный вывод сети RCF генерируется путем слияния 5 побочных выходов, поэтому вывод вашей RCF также должен быть больше, чемн\etaсчитается положительным, то меньшен\etaсчитается отрицательным.На самом деле я этого не учел, когда сам воспроизводил.Не учел этого в гитхабе и официальном коде в интернете.Они все прям кросс-энтропия. . . Я также объясню это в статье без дальнейших церемоний.н\etaзначение

3 pytorch часть кода

Для этой статьи RCF ключом является построение модели, а другой - построение функции потерь.Вот две части кода, которые помогут вам лучше понять вышеизложенное.

3.1 Модельная часть

Следующий код в части апсэмплинга написан по старинке, потому что найденная в интернете версия pytorch оценивается как старая.На тот момент не было пакета функций типа Conv2DTrans, но это не помешало вам изучить RCF через код.

class RCF(nn.Module):
    def __init__(self):
        super(RCF, self).__init__()
        #lr 1 2 decay 1 0
        self.conv1_1 = nn.Conv2d(3, 64, 3, padding=1)
        self.conv1_2 = nn.Conv2d(64, 64, 3, padding=1)

        self.conv2_1 = nn.Conv2d(64, 128, 3, padding=1)
        self.conv2_2 = nn.Conv2d(128, 128, 3, padding=1)

        self.conv3_1 = nn.Conv2d(128, 256, 3, padding=1)
        self.conv3_2 = nn.Conv2d(256, 256, 3, padding=1)
        self.conv3_3 = nn.Conv2d(256, 256, 3, padding=1)

        self.conv4_1 = nn.Conv2d(256, 512, 3, padding=1)
        self.conv4_2 = nn.Conv2d(512, 512, 3, padding=1)
        self.conv4_3 = nn.Conv2d(512, 512, 3, padding=1)

        self.conv5_1 = nn.Conv2d(512, 512, kernel_size=3,
                        stride=1, padding=2, dilation=2)
        self.conv5_2 = nn.Conv2d(512, 512, kernel_size=3,
                        stride=1, padding=2, dilation=2)
        self.conv5_3 = nn.Conv2d(512, 512, kernel_size=3,
                        stride=1, padding=2, dilation=2)
        self.relu = nn.ReLU()
        self.maxpool = nn.MaxPool2d(2, stride=2, ceil_mode=True)
        self.maxpool4 = nn.MaxPool2d(2, stride=1, ceil_mode=True)


        #lr 0.1 0.2 decay 1 0
        self.conv1_1_down = nn.Conv2d(64, 21, 1, padding=0)
        self.conv1_2_down = nn.Conv2d(64, 21, 1, padding=0)

        self.conv2_1_down = nn.Conv2d(128, 21, 1, padding=0)
        self.conv2_2_down = nn.Conv2d(128, 21, 1, padding=0)

        self.conv3_1_down = nn.Conv2d(256, 21, 1, padding=0)
        self.conv3_2_down = nn.Conv2d(256, 21, 1, padding=0)
        self.conv3_3_down = nn.Conv2d(256, 21, 1, padding=0)

        self.conv4_1_down = nn.Conv2d(512, 21, 1, padding=0)
        self.conv4_2_down = nn.Conv2d(512, 21, 1, padding=0)
        self.conv4_3_down = nn.Conv2d(512, 21, 1, padding=0)
        
        self.conv5_1_down = nn.Conv2d(512, 21, 1, padding=0)
        self.conv5_2_down = nn.Conv2d(512, 21, 1, padding=0)
        self.conv5_3_down = nn.Conv2d(512, 21, 1, padding=0)

        #lr 0.01 0.02 decay 1 0
        self.score_dsn1 = nn.Conv2d(21, 1, 1)
        self.score_dsn2 = nn.Conv2d(21, 1, 1)
        self.score_dsn3 = nn.Conv2d(21, 1, 1)
        self.score_dsn4 = nn.Conv2d(21, 1, 1)
        self.score_dsn5 = nn.Conv2d(21, 1, 1)
        #lr 0.001 0.002 decay 1 0
        self.score_final = nn.Conv2d(5, 1, 1)

    def forward(self, x):
        # VGG
        img_H, img_W = x.shape[2], x.shape[3]
        conv1_1 = self.relu(self.conv1_1(x))
        conv1_2 = self.relu(self.conv1_2(conv1_1))
        pool1   = self.maxpool(conv1_2)

        conv2_1 = self.relu(self.conv2_1(pool1))
        conv2_2 = self.relu(self.conv2_2(conv2_1))
        pool2   = self.maxpool(conv2_2)

        conv3_1 = self.relu(self.conv3_1(pool2))
        conv3_2 = self.relu(self.conv3_2(conv3_1))
        conv3_3 = self.relu(self.conv3_3(conv3_2))
        pool3   = self.maxpool(conv3_3)

        conv4_1 = self.relu(self.conv4_1(pool3))
        conv4_2 = self.relu(self.conv4_2(conv4_1))
        conv4_3 = self.relu(self.conv4_3(conv4_2))
        pool4   = self.maxpool4(conv4_3)

        conv5_1 = self.relu(self.conv5_1(pool4))
        conv5_2 = self.relu(self.conv5_2(conv5_1))
        conv5_3 = self.relu(self.conv5_3(conv5_2))

        conv1_1_down = self.conv1_1_down(conv1_1)
        conv1_2_down = self.conv1_2_down(conv1_2)
        conv2_1_down = self.conv2_1_down(conv2_1)
        conv2_2_down = self.conv2_2_down(conv2_2)
        conv3_1_down = self.conv3_1_down(conv3_1)
        conv3_2_down = self.conv3_2_down(conv3_2)
        conv3_3_down = self.conv3_3_down(conv3_3)
        conv4_1_down = self.conv4_1_down(conv4_1)
        conv4_2_down = self.conv4_2_down(conv4_2)
        conv4_3_down = self.conv4_3_down(conv4_3)
        conv5_1_down = self.conv5_1_down(conv5_1)
        conv5_2_down = self.conv5_2_down(conv5_2)
        conv5_3_down = self.conv5_3_down(conv5_3)

        so1_out = self.score_dsn1(conv1_1_down + conv1_2_down)
        so2_out = self.score_dsn2(conv2_1_down + conv2_2_down)
        so3_out = self.score_dsn3(conv3_1_down + conv3_2_down + conv3_3_down)
        so4_out = self.score_dsn4(conv4_1_down + conv4_2_down + conv4_3_down)
        so5_out = self.score_dsn5(conv5_1_down + conv5_2_down + conv5_3_down)
        ## transpose and crop way 
        weight_deconv2 =  make_bilinear_weights(4, 1).cuda()
        weight_deconv3 =  make_bilinear_weights(8, 1).cuda()
        weight_deconv4 =  make_bilinear_weights(16, 1).cuda()
        weight_deconv5 =  make_bilinear_weights(32, 1).cuda()

        upsample2 = torch.nn.functional.conv_transpose2d(so2_out, weight_deconv2, stride=2)
        upsample3 = torch.nn.functional.conv_transpose2d(so3_out, weight_deconv3, stride=4)
        upsample4 = torch.nn.functional.conv_transpose2d(so4_out, weight_deconv4, stride=8)
        upsample5 = torch.nn.functional.conv_transpose2d(so5_out, weight_deconv5, stride=8)
        ### center crop
        so1 = crop(so1_out, img_H, img_W)
        so2 = crop(upsample2, img_H, img_W)
        so3 = crop(upsample3, img_H, img_W)
        so4 = crop(upsample4, img_H, img_W)
        so5 = crop(upsample5, img_H, img_W)

        fusecat = torch.cat((so1, so2, so3, so4, so5), dim=1)
        fuse = self.score_final(fusecat)
        results = [so1, so2, so3, so4, so5, fuse]
        results = [torch.sigmoid(r) for r in results]
        return results

3.2 Часть функции потерь

def cross_entropy_loss_RCF(prediction, label):
    label = label.long()
    mask = label.float()
    num_positive = torch.sum((mask==1).float()).float()
    num_negative = torch.sum((mask==0).float()).float()

    mask[mask == 1] = 1.0 * num_negative / (num_positive + num_negative)
    mask[mask == 0] = 1.1 * num_positive / (num_positive + num_negative)
    mask[mask == 2] = 0
    cost = torch.nn.functional.binary_cross_entropy(
            prediction.float(),label.float(), weight=mask, reduce=False)
    return torch.sum(cost)

Справочная статья:

  1. blog.CSDN.net/ah8039974/AR…
  2. git ee.com/heart1/R cf-…
  3. открытый доступ. Город Тяньхэ VF.com/content_CV с…