class_docstr: Feature extractor with additional layers to append.
  This is specified for ``Feature Pyramid Network for Object Detection``
  which implement ``Top-down pathway and lateral connections``.
  
  Parameters
  ----------
  network : str or HybridBlock or Symbol
      Logic chain: load from gluon.model_zoo.vision if network is string.
      Convert to Symbol if network is HybridBlock.
  outputs : str or list of str
      The name of layers to be extracted as features
  num_filters : list of int e.g. [256, 256, 256, 256]
      Number of filters to be appended.
  use_1x1 : bool
      Whether to use 1x1 convolution
  use_upsample : bool
      Whether to use upsample
  use_elewadd : float
      Whether to use element-wise add operation
  use_p6 : bool
      Whether use P6 stage, this is used for RPN experiments in ori paper
  p6_conv : bool
      Whether to use convolution for P6 stage, if it is enabled, or just max pooling.
  no_bias : bool
      Whether use bias for Convolution operation.
  norm_layer : HybridBlock or SymbolBlock
      Type of normalization layer.
  norm_kwargs : dict
      Arguments for normalization layer.
  pretrained : bool
      Use pretrained parameters as in gluon.model_zoo if `True`.
  ctx : Context
      The context, e.g. mxnet.cpu(), mxnet.gpu(0).
  inputs : list of str
      Name of input variables to the network.
code: def __init__(
      self,
      network,
      outputs,
      num_filters,
      use_1x1=True,
      use_upsample=True,
      use_elewadd=True,
      use_p6=False,
      p6_conv=True,
      no_bias=True,
      pretrained=False,
      norm_layer=None,
      norm_kwargs=None,
      ctx=mx.cpu(),
      inputs=("data",),
  ):
      inputs, outputs, params = _parse_network(
          network, outputs, inputs, pretrained, ctx
      )
      if norm_kwargs is None:
          norm_kwargs = {}
      # e.g. For ResNet50, the feature is :
      # outputs = ['stage1_activation2', 'stage2_activation3',
      #            'stage3_activation5', 'stage4_activation2']
      # with regard to [conv2, conv3, conv4, conv5] -> [C2, C3, C4, C5]
      # append more layers with reversed order : [P5, P4, P3, P2]
      y = outputs[-1]
      base_features = outputs[::-1]
      num_stages = len(num_filters) + 1  # usually 5
      weight_init = mx.init.Xavier(
          rnd_type="uniform", factor_type="in", magnitude=1.0
      )
      tmp_outputs = []
      # num_filter is 256 in ori paper
      for i, (bf, f) in enumerate(zip(base_features, num_filters)):
          if i == 0:
              if use_1x1:
                  y = mx.sym.Convolution(
                      y,
                      num_filter=f,
                      kernel=(1, 1),
                      pad=(0, 0),
                      stride=(1, 1),
                      no_bias=no_bias,
                      name="P{}_conv_lat".format(num_stages - i),
                      attr={"__init__": weight_init},
                  )
                  if norm_layer is not None:
                      if norm_layer is SyncBatchNorm:
                          norm_kwargs["key"] = "P{}_lat_bn".format(
                              num_stages - i
                          )
                          norm_kwargs["name"] = "P{}_lat_bn".format(
                              num_stages - i
                          )
                      y = norm_layer(y, **norm_kwargs)
              if use_p6 and p6_conv:
                  # method 2 : use conv (Deformable use this)
                  y_p6 = mx.sym.Convolution(
                      y,
                      num_filter=f,
                      kernel=(3, 3),
                      pad=(1, 1),
                      stride=(2, 2),
                      no_bias=no_bias,
                      name="P{}_conv1".format(num_stages + 1),
                      attr={"__init__": weight_init},
                  )
                  if norm_layer is not None:
                      if norm_layer is SyncBatchNorm:
                          norm_kwargs["key"] = "P{}_pre_bn".format(
                              num_stages + 1
                          )
                          norm_kwargs["name"] = "P{}_pre_bn".format(
                              num_stages + 1
                          )
                      y_p6 = norm_layer(y_p6, **norm_kwargs)
          else:
              if use_1x1:
                  bf = mx.sym.Convolution(
                      bf,
                      num_filter=f,
                      kernel=(1, 1),
                      pad=(0, 0),
                      stride=(1, 1),
                      no_bias=no_bias,
                      name="P{}_conv_lat".format(num_stages - i),
                      attr={"__init__": weight_init},
                  )
                  if norm_layer is not None:
                      if norm_layer is SyncBatchNorm:
                          norm_kwargs["key"] = "P{}_conv1_bn".format(
                              num_stages - i
                          )
                          norm_kwargs["name"] = "P{}_conv1_bn".format(
                              num_stages - i
                          )
                      bf = norm_layer(bf, **norm_kwargs)
              if use_upsample:
                  y = mx.sym.UpSampling(
                      y,
                      scale=2,
                      sample_type="nearest",
                      name="P{}_upsp".format(num_stages - i),
                  )
  
              if use_elewadd:
                  # make two symbol alignment
                  # method 1 : mx.sym.Crop
                  # y = mx.sym.Crop(*[y, bf], name="P{}_clip".format(num_stages-i))
                  # method 2 : mx.sym.slice_like
                  y = mx.sym.slice_like(
                      y,
                      bf,
                      axes=(2, 3),
                      name="P{}_clip".format(num_stages - i),
                  )
                  y = mx.sym.ElementWiseSum(
                      bf, y, name="P{}_sum".format(num_stages - i)
                  )
          # Reduce the aliasing effect of upsampling described in ori paper
          out = mx.sym.Convolution(
              y,
              num_filter=f,
              kernel=(3, 3),
              pad=(1, 1),
              stride=(1, 1),
              no_bias=no_bias,
              name="P{}_conv1".format(num_stages - i),
              attr={"__init__": weight_init},
          )
          if i == 0 and use_p6 and not p6_conv:
              # method 2 : use max pool (Detectron use this)
              y_p6 = mx.sym.Pooling(
                  out,
                  pool_type="max",
                  kernel=(1, 1),
                  pad=(0, 0),
                  stride=(2, 2),
                  name="P{}_pre".format(num_stages + 1),
              )
          if norm_layer is not None:
              if norm_layer is SyncBatchNorm:
                  norm_kwargs["key"] = "P{}_bn".format(num_stages - i)
                  norm_kwargs["name"] = "P{}_bn".format(num_stages - i)
              out = norm_layer(out, **norm_kwargs)
          tmp_outputs.append(out)
      if use_p6:
          outputs = tmp_outputs[::-1] + [
              y_p6
          ]  # [P2, P3, P4, P5] + [P6]
      else:
          outputs = tmp_outputs[::-1]  # [P2, P3, P4, P5]
  
      super(FPNFeatureExpander, self).__init__(outputs, inputs, params)
  
file_path: gluon-cv/gluoncv/nn/feature.py
imports: from __future__ import absolute_import
  import mxnet
  from mxnet.base import string_types
  from mxnet.gluon import HybridBlock, SymbolBlock
  from mxnet.symbol import Symbol
  from mxnet.symbol.contrib import SyncBatchNorm
label_desc: Use image node_id 1 to create a new FPNFeatureExpander object from inherited base classes: SymbolBlock with example: obj = FPNFeatureExpander(network, outputs, num_filters, use_1x1, use_upsample, use_elewadd, use_p6, p6_conv, no_bias, pretrained, norm_layer, norm_kwargs, ctx, inputs)