Есть ли побочный эффект от использования 'Sequential ()' или 'HybridSequential ()' только в качестве контейнера? - PullRequest
0 голосов
/ 22 мая 2018

Я читаю учебник о MxNet.Авторы используют mxnet.gluon.nn.Sequential () в качестве контейнера для хранения некоторых блоков (см. Код 1);затем они перепишут соединение блоков в «def forward (self, x)» (см. коды 2 и 3).Есть ли побочный эффект от этого?Кстати, в чем разница между «Sequential ()» и «HybridSequential ()».Я пытаюсь использовать список, чтобы заменить «Последовательный», и получаю следующие предупреждения в процессе инициализации.

«ToySSD.downsamplers» - это контейнер с блоками.Обратите внимание, что блоки внутри списка, кортежа или dict не будут зарегистрированы автоматически.Обязательно зарегистрируйте их, используя register_child () или переключившись на nn.Sequential / nn.HybridSequential. '

Насколько я знаю, если вы поместите несколько блоков в' mxnet.gluon.nn.Sequential () 'или' mxnet.gluon.nn.HybridSequential () ', это действие сообщает компьютеру, что эти блоки подключены.Однако, если вы проектируете взаимосвязь блоков в функции «forward», вы говорите компьютеру подключить эти блоки другим способом.Приведет ли это к путанице?Если я спроектирую только некоторые соединения блоков в «forward», каковы взаимосвязи других блоков в «Sequential ()», которые не предназначены для функции «forward»?

Весь учебник можно найти в здесь .

код 1:

def toy_ssd_model(num_anchors, num_classes):
    downsamplers = nn.Sequential()
    for _ in range(3):
        downsamplers.add(down_sample(128))

    class_predictors = nn.Sequential()
    box_predictors = nn.Sequential()    
    for _ in range(5):
        class_predictors.add(class_predictor(num_anchors, num_classes))
        box_predictors.add(box_predictor(num_anchors))

    model = nn.Sequential()
    model.add(body(), downsamplers, class_predictors, box_predictors)
    return model

код 2:

def toy_ssd_forward(x, model, sizes, ratios, verbose=False):    
    body, downsamplers, class_predictors, box_predictors = model
    anchors, class_preds, box_preds = [], [], []
    # feature extraction    
    x = body(x)
    for i in range(5):
        # predict
        anchors.append(MultiBoxPrior(
            x, sizes=sizes[i], ratios=ratios[i]))
        class_preds.append(
            flatten_prediction(class_predictors[i](x)))
        box_preds.append(
            flatten_prediction(box_predictors[i](x)))
        if verbose:
            print('Predict scale', i, x.shape, 'with', 
                  anchors[-1].shape[1], 'anchors')
        # down sample
        if i < 3:
            x = downsamplers[i](x)
        elif i == 3:
            x = nd.Pooling(
                x, global_pool=True, pool_type='max', 
                kernel=(x.shape[2], x.shape[3]))
    # concat data
    return (concat_predictions(anchors),
            concat_predictions(class_preds),
            concat_predictions(box_preds))

код 3:

from mxnet import gluon
class ToySSD(gluon.Block):
    def __init__(self, num_classes, verbose=False, **kwargs):
        super(ToySSD, self).__init__(**kwargs)
        # anchor box sizes and ratios for 5 feature scales
        self.sizes = [[.2,.272], [.37,.447], [.54,.619], 
                      [.71,.79], [.88,.961]]
        self.ratios = [[1,2,.5]]*5
        self.num_classes = num_classes
        self.verbose = verbose
        num_anchors = len(self.sizes[0]) + len(self.ratios[0]) - 1
        # use name_scope to guard the names
        with self.name_scope():
            self.model = toy_ssd_model(num_anchors, num_classes)

    def forward(self, x):
        anchors, class_preds, box_preds = toy_ssd_forward(
            x, self.model, self.sizes, self.ratios, 
            verbose=self.verbose)
        # it is better to have class predictions reshaped for softmax computation       
        class_preds = class_preds.reshape(shape=(0, -1, self.num_classes+1))
        return anchors, class_preds, box_preds

1 Ответ

0 голосов
/ 23 мая 2018

В Gluon сети строятся с использованием Block s.Если что-то не является Block, оно не может быть частью сети Gluon.Плотный слой - Block, Convolution - Block, пуловый слой - Block и т. Д.

Иногда вам может потребоваться блок, который не является предварительно определенным блоком в Gluon, но являетсяпоследовательность предопределенных блоков Gluon.Например,

Conv2D -> MaxPool2D -> Conv2D -> MaxPool2D -> Flatten -> Dense -> Dense

Gluon не имеет предварительно определенного блока, который выполняет вышеуказанную последовательность операций.Но у Gluon есть блоки, которые выполняют каждую отдельную операцию.Таким образом, вы можете создать свой собственный блок, который выполняет описанную выше последовательность операций, связав воедино предопределенные блоки Gluon.Пример:

net = gluon.nn.HybridSequential()

with net.name_scope():

    # First convolution
    net.add(gluon.nn.Conv2D(channels=20, kernel_size=5, activation='relu'))
    net.add(gluon.nn.MaxPool2D(pool_size=2, strides=2))

    # Second convolution
    net.add(gluon.nn.Conv2D(channels=50, kernel_size=5, activation='relu'))
    net.add(gluon.nn.MaxPool2D(pool_size=2, strides=2))

    # Flatten the output before the fully connected layers
    net.add(gluon.nn.Flatten())

    # First fully connected layers with 512 neurons
    net.add(gluon.nn.Dense(512, activation="relu"))

    # Second fully connected layer with as many neurons as the number of classes
    net.add(gluon.nn.Dense(num_outputs))

Когда вы создаете такую ​​последовательность, вы можете использовать HybridSequential или Sequential.Чтобы понять разницу, вам нужно понять разницу между символическим и императивным программированием .

  • HybridBlock - это блок, который можно преобразовать в символьный граф для более быстрого выполнения.HybridSequential - это последовательность Hybrid блоков.
  • Blocks (не гибридных) - это блок, который нельзя преобразовать в символьный граф.Sequential - это последовательность негибридных блоков.

То, является ли блок гибридным, зависит от того, как он реализован.Почти все предопределенные блоки Gluon также являются HybridBlocks.Иногда есть причина, по которой некоторые блоки не могут быть гибридными. Дерево LSTM является одним примером.Чаще всего что-то не является гибридом только потому, что тот, кто написал его, не пытался сделать его гибридным по нескольким причинам (например: создание гибридного не даст большого прироста производительности или создание гибридного блока сложно).

Обратите внимание, что Sequential и HybridSequential - это не просто контейнеры, такие как Python list.Когда вы используете один из них, вы фактически создаете новый Block, используя уже существующие блоки.Вот почему вы не можете заменить Sequential с помощью Python list.

Хорошо, так что вы знаете, как создать свой собственный блок, соединив вместе существующие блоки.Хорошо.Что если вы хотите не просто передавать данные через последовательность блоков?Что делать, если вы хотите условно передать данные через один из этих блоков.Вот пример из ResNet:

class BasicBlockV1(HybridBlock):
    def __init__(self, channels, stride, downsample=False, in_channels=0, **kwargs):
        super(BasicBlockV1, self).__init__(**kwargs)
        self.body = nn.HybridSequential(prefix='')
        self.body.add(_conv3x3(channels, stride, in_channels))
        self.body.add(nn.BatchNorm())
        self.body.add(nn.Activation('relu'))
        self.body.add(_conv3x3(channels, 1, channels))
        self.body.add(nn.BatchNorm())
        if downsample:
            self.downsample = nn.HybridSequential(prefix='')
            self.downsample.add(nn.Conv2D(channels, kernel_size=1, strides=stride,
                                          use_bias=False, in_channels=in_channels))
            self.downsample.add(nn.BatchNorm())
        else:
            self.downsample = None

    def hybrid_forward(self, F, x):
        residual = x

        x = self.body(x)

        if self.downsample:
            residual = self.downsample(residual)

        x = F.Activation(residual+x, act_type='relu')

        return x

Этот код создает новый блок, используя ранее существующие блоки Gluon.Но это больше, чем просто прохождение данных через некоторые существующие блоки.Учитывая некоторые данные, блок пропускает данные через тело block в пути.Но затем запускает данные через downsample, только если этот блок был создан с downsample, установленным в значение true.Затем он объединяет выходные данные body и downsample, чтобы создать выходные данные.Как вы можете видеть, что происходит нечто большее, чем просто передача данных через последовательность блоков.Это когда вы создаете свой собственный блок подклассами HybridBlock или Block.

Обратите внимание, что функция __init__ создала необходимые блоки, а функция forward получает входные данные и пропускает входные данные через блоки.создан в __init__.forward не изменяет блоки, созданные в __init__.Он пропускает данные только через блоки, созданные в __init__.

. В приведенном вами примере первый блок кода создает блоки типа downsamplers, class_predictors, box_predictors.Функции пересылки в кодовом блоке 2 и 3 не модифицируют эти блоки.Они просто передают входные данные через эти блоки.

...