예제 #1
0
    def _depthwise_conv_block(inputs,
                              n_groups,
                              pointwise_conv_filters,
                              alpha,
                              depth_multiplier=1,
                              stride=1,
                              block_id=1):
        """Adds a depthwise convolution block.

        inputs:
            Input tensor
        n_groups : int
            number of groups
        pointwise_conv_filters:
            the dimensionality of the output space
        alpha: controls the width of the network.
            - If `alpha` < 1.0, proportionally decreases the number
                of filters in each layer.
            - If `alpha` > 1.0, proportionally increases the number
                of filters in each layer.
            - If `alpha` = 1, default number of filters from the paper
                 are used at each layer.
        depth_multiplier:
            The number of depthwise convolution output channels
        strides: An integer or tuple/list of 2 integers,
            specifying the strides of the convolution
        block_id: Integer, a unique identification designating
            the block number.

        """
        pointwise_conv_filters = int(pointwise_conv_filters * alpha)

        x = GroupConv2d(n_groups * depth_multiplier,
                        n_groups,
                        3,
                        stride=stride,
                        act='identity',
                        include_bias=False,
                        name='conv_dw_%d' % block_id)(inputs)
        x = BN(name='conv_dw_%d_bn' % block_id, act='relu')(x)

        x = Conv2d(pointwise_conv_filters,
                   1,
                   act='identity',
                   include_bias=False,
                   stride=1,
                   name='conv_pw_%d' % block_id)(x)
        x = BN(name='conv_pw_%d_bn' % block_id, act='relu')(x)
        return x, pointwise_conv_filters
예제 #2
0
    def _inverted_res_block(inputs, in_channels, expansion, stride, alpha,
                            filters, block_id):
        """
        Inverted Residual Block

        Parameters
        ----------
        inputs:
            Input tensor
        in_channels:
            Specifies the number of input tensor's channel
        expansion:
            expansion factor always applied to the input size.
        stride:
            the strides of the convolution
        alpha:
            width multiplier.
        filters:
            the dimensionality of the output space.
        block_id:
            block id used for naming layers

        """
        pointwise_conv_filters = int(filters * alpha)
        pointwise_filters = _make_divisible(pointwise_conv_filters, 8)
        x = inputs
        prefix = 'block_{}_'.format(block_id)
        n_groups = in_channels

        if block_id:
            # Expand
            n_groups = expansion * in_channels
            x = Conv2d(expansion * in_channels,
                       1,
                       include_bias=False,
                       act='identity',
                       name=prefix + 'expand')(x)
            x = BN(name=prefix + 'expand_BN', act='identity')(x)
        else:
            prefix = 'expanded_conv_'

        # Depthwise
        x = GroupConv2d(n_groups,
                        n_groups,
                        3,
                        stride=stride,
                        act='identity',
                        include_bias=False,
                        name=prefix + 'depthwise')(x)
        x = BN(name=prefix + 'depthwise_BN', act='relu')(x)

        # Project
        x = Conv2d(pointwise_filters,
                   1,
                   include_bias=False,
                   act='identity',
                   name=prefix + 'project')(x)
        x = BN(name=prefix + 'project_BN',
               act='identity')(x)  # identity activation on narrow tensor

        if in_channels == pointwise_filters and stride == 1:
            return Res(name=prefix + 'add')([inputs, x]), pointwise_filters
        return x, pointwise_filters
예제 #3
0
 def test_group_conv1(self):
     group_conv1 = GroupConv2d(n_filters=30,
                               n_groups=3,
                               stride_horizontal=2)
     self.assertTrue(group_conv1.config['nGroups'] == 3)
예제 #4
0
    def _MBConvBlock(inputs, in_channels, out_channels, ksize, stride, expansion, se_ratio, stage_id, block_id,
                     noskip=False, activation_fn='relu'):
        '''
        Inverted Residual Block

        Parameters
        ----------
        inputs: input tensor
            Speecify input tensor for block.
        in_channels: integer
            Specifies the number of input tensor's channel.
        out_channels: integer
            Specifies the number of output tensor's channel
        ksize:
            Specifies the kernel size of the convolution
        stride: integer
            Specifies the stride of the convolution
        expansion: double
            Specifies the expansion factor for the input layer.
        se_ratio: double
            Specifies the ratio to squeeze the input filters for squeeze-and-excitation block.
        stage_id: integer
            Specifies stage id for naming layers
        block_id:
            Specifies block id for naming layers
        noskip: bool
            Specifies whether the skip connection is used. By default, the skip connection is used.
        activation_fn:
            Specifies activation function
        '''

        # mobilenetv2 block is also known as inverted residual block, which consists of three convolutions:
        # the first is 1*1 convolution for expansion
        # the second is depthwise convolution
        # the third is 1*1 convolution without any non-linearity for projection

        x = inputs
        prefix = 'stage_{}_block_{}'.format(stage_id, block_id)
        n_groups = in_channels  # for expansion=1, n_groups might be different from pointwise_filters

        if expansion > 1:
            # For MobileNet V2, expansion>1 when stage>0
            n_groups = int(expansion * in_channels)  ## update n_groups
            x = Conv2d(n_groups, 1, include_bias=False, act='identity',
                       name=prefix + 'expand')(x)
            x = BN(name=prefix + 'expand_BN', act='identity')(x)

        # Depthwise convolution
        x = GroupConv2d(n_groups, n_groups, ksize, stride=stride, act='identity',
                        include_bias=False, name=prefix + 'depthwise')(x)
        x = BN(name=prefix + 'depthwise_BN', act=activation_fn)(x)

        # Squeeze-Excitation
        if 0 < se_ratio <= 1:
            se_input = x  # features to be squeezed
            x = GlobalAveragePooling2D(name=prefix + "global_avg_pool")(x)
            # Squeeze
            channels_se = max(1, int(in_channels * se_ratio))
            x = Conv2d(channels_se, 1, include_bias=True, act=activation_fn, name=prefix + 'squeeze')(x)
            x = Conv2d(n_groups, 1, include_bias=True, act='sigmoid', name=prefix + 'excitation')(x)
            x = Reshape(name=prefix + 'reshape', width=n_groups, height=1, depth=1)(x)
            x = Scale(name=prefix + 'scale')([se_input, x])  # x = out*w

        # Project
        x = Conv2d(out_channels, 1, include_bias=False, act='identity', name=prefix + 'project')(x)
        x = BN(name=prefix + 'project_BN', act='identity')(x)  # identity activation on narrow tensor
        # Prepare output for MBConv block
        if in_channels == out_channels and stride == 1 and (not noskip):
            # dropout can be added.
            return Res(name=prefix + 'add_se_residual')([x, inputs])
        else:
            return x
예제 #5
0
    def _shuffle_unit(inputs,
                      in_channels,
                      out_channels,
                      groups,
                      bottleneck_ratio,
                      strides=2,
                      stage=1,
                      block=1):
        """
        create a shuffle unit

        Parameters
        ----------
        inputs:
            Input tensor of with `channels_last` data format
        in_channels:
            number of input channels
        out_channels:
            number of output channels
        strides:
            An integer or tuple/list of 2 integers,
        groups:
            number of groups per channel
        bottleneck_ratio: float
            bottleneck ratio implies the ratio of bottleneck channels to output channels.
        stage:
            stage number
        block:
            block number

        """
        prefix = 'stage%d/block%d' % (stage, block)

        # if strides >= 2:
        # out_channels -= in_channels

        # default: 1/4 of the output channel of a ShuffleNet Unit
        bottleneck_channels = int(out_channels * bottleneck_ratio)
        groups = (1 if stage == 2 and block == 1 else groups)

        # x = _group_conv(inputs, in_channels, out_channels = bottleneck_channels,
        #                 groups = (1 if stage == 2 and block == 1 else groups),
        #                 name = '%s/1x1_gconv_1' % prefix)

        x = GroupConv2d(bottleneck_channels,
                        n_groups=(1 if stage == 2 and block == 1 else groups),
                        act='identity',
                        width=1,
                        height=1,
                        stride=1,
                        include_bias=False,
                        name='%s/1x1_gconv_1' % prefix)(inputs)

        x = BN(act='relu', name='%s/bn_gconv_1' % prefix)(x)

        x = ChannelShuffle(n_groups=groups,
                           name='%s/channel_shuffle' % prefix)(x)
        # depthwise convolutioin
        x = GroupConv2d(x.shape[-1],
                        n_groups=x.shape[-1],
                        width=3,
                        height=3,
                        include_bias=False,
                        stride=strides,
                        act='identity',
                        name='%s/1x1_dwconv_1' % prefix)(x)
        x = BN(act=block_act, name='%s/bn_dwconv_1' % prefix)(x)

        out_channels = out_channels if strides == 1 else out_channels - in_channels
        x = GroupConv2d(out_channels,
                        n_groups=groups,
                        width=1,
                        height=1,
                        stride=1,
                        act='identity',
                        include_bias=False,
                        name='%s/1x1_gconv_2' % prefix)(x)

        x = BN(act=block_act, name='%s/bn_gconv_2' % prefix)(x)

        if strides < 2:
            ret = Res(act='relu', name='%s/add' % prefix)([x, inputs])
        else:
            avg = Pooling(width=3,
                          height=3,
                          stride=2,
                          pool='mean',
                          name='%s/avg_pool' % prefix)(inputs)
            ret = Concat(act='relu', name='%s/concat' % prefix)([x, avg])

        return ret
예제 #6
0
def onnx_extract_conv(graph, node, layers):
    ''' 
    Construct convo layer from ONNX op 

    Parameters
    ----------
    graph : ONNX GraphProto
        Specifies a GraphProto object.
    node : ONNX NodeProto
        Specifies a NodeProto object.
    layers : list of Layers
        Specifies the existing layers of a model.

    Returns
    -------
    :class:`Conv2d` or 'GroupConv2d'

    '''
    previous = onnx_find_previous_compute_layer(graph, node)

    if not previous:
        src_names = [find_input_layer_name(graph)]
    else:
        src_names = [p.name for p in previous]

    src = [get_dlpy_layer(layers, i) for i in src_names]

    height = None
    width = None
    stride = None
    stride_horizontal = None
    stride_vertical = None
    padding = None
    padding_height = None
    padding_width = None
    n_filters = None
    include_bias = False
    act = 'identity'
    group = None

    # if padding is not present, default to 0
    is_padding = False

    attributes = node.attribute
    for attr in attributes:
        if attr.name == 'kernel_shape':
            height, width = attr.ints
        elif attr.name == 'strides':
            stride_vertical, stride_horizontal = attr.ints
            # only specify one of stride and stride_horizontal
            if stride_horizontal == stride_vertical:
                stride = stride_horizontal
                stride_horizontal = None
                stride_vertical = None
        elif attr.name == 'auto_pad':
            is_padding = True
            attr_s = attr.s.decode('utf8')
            if attr_s == 'SAME_UPPER' or attr_s == 'SAME_LOWER':
                continue
            elif attr_s == 'NOTSET':
                continue
            else:  # 'VALID'
                padding = 0
        elif attr.name == 'pads':
            is_padding = True
            padding_height, padding_width, p_h2, p_w2 = attr.ints
            if padding_height != p_h2 or padding_width != p_w2:
                print('Warning: Unequal padding not supported for ' +
                      node.name + ' setting equal padding instead.')
                padding_height = max(padding_height, p_h2)
                padding_width = max(padding_width, p_w2)
        elif attr.name == 'group':
            group = attr.i

    if not is_padding:
        padding = 0

    # check if weight tensor is in initializer
    for init in graph.initializer:
        if init.name == node.input[1]:
            n_filters = numpy_helper.to_array(init).shape[0]

    # if not in initializer, check inferred shapes in graph
    if n_filters is None:
        for v in graph.value_info:
            if v.name == node.input[1]:
                n_filters = v.type.tensor_type.shape.dim[0].dim_value

    # check if bias is specified in conv op
    if len(node.input) == 3:
        include_bias = True
    # check if bias is added by the next op
    else:
        out = onnx_get_out_nodes(graph, node)
        for n in out:
            if is_bias_op(graph, n):
                include_bias = True

    if group and group > 1:
        return GroupConv2d(n_groups=group,
                           n_filters=n_filters,
                           width=width,
                           height=height,
                           stride=stride,
                           name=node.name,
                           stride_horizontal=stride_horizontal,
                           stride_vertical=stride_vertical,
                           padding=padding,
                           padding_width=padding_width,
                           padding_height=padding_height,
                           act=act,
                           include_bias=include_bias,
                           src_layers=src)
    else:
        return Conv2d(n_filters=n_filters,
                      width=width,
                      height=height,
                      stride=stride,
                      name=node.name,
                      stride_horizontal=stride_horizontal,
                      stride_vertical=stride_vertical,
                      padding=padding,
                      padding_width=padding_width,
                      padding_height=padding_height,
                      act=act,
                      include_bias=include_bias,
                      src_layers=src)