예제 #1
0
def group_norm_symbolic(g, input, num_groups, weight, bias, eps,
                        cudnn_enabled):
    from torch.onnx.symbolic_opset9 import reshape, mul, add, reshape_as

    channels_num = input.type().sizes()[1]

    if num_groups == channels_num:
        output = g.op('InstanceNormalization',
                      input,
                      weight,
                      bias,
                      epsilon_f=eps)
    else:
        # Reshape from [n, g * cg, h, w] to [1, n * g, cg * h, w].
        x = reshape(g, input, [0, num_groups, -1, 0])
        x = reshape(g, x, [1, -1, 0, 0])
        # Normalize channel-wise.
        x = g.op('MeanVarianceNormalization', x, axes_i=[2, 3])
        # Reshape back.
        x = reshape_as(g, x, input)
        # Apply affine transform.
        x = mul(g, x, reshape(g, weight, [1, channels_num, 1, 1]))
        output = add(g, x, reshape(g, bias, [1, channels_num, 1, 1]))

    return output
예제 #2
0
def nms_core_symbolic(g, dets, iou_thr, score_thr, max_num):

    from torch.onnx.symbolic_opset9 import reshape, unsqueeze, squeeze
    from torch.onnx.symbolic_opset10 import _slice

    assert 0 <= iou_thr <= 1
    multi_bboxes = _slice(g, dets, axes=[1], starts=[0], ends=[4])
    # multi_bboxes = unsqueeze(g, multi_bboxes, 0)
    multi_bboxes = reshape(g, multi_bboxes, [1, -1, 4])
    multi_scores = _slice(g, dets, axes=[1], starts=[4], ends=[5])
    multi_scores = reshape(g, multi_scores, [1, 1, -1])

    assert max_num > 0

    indices = g.op('NonMaxSuppression', multi_bboxes, multi_scores,
                   g.op('Constant', value_t=torch.LongTensor([max_num])),
                   g.op('Constant', value_t=torch.FloatTensor([iou_thr])),
                   g.op('Constant', value_t=torch.FloatTensor([score_thr])))
    indices = squeeze(g, _slice(g, indices, axes=[1], starts=[2], ends=[3]), 1)

    # Sort indices by score.
    scores = reshape(g, multi_scores, [
        -1,
    ])
    keeped_scores = g.op('Gather', scores, indices, axis_i=0)
    elements_num = sym_help._size_helper(g,
                                         keeped_scores,
                                         dim=g.op('Constant',
                                                  value_t=torch.LongTensor(
                                                      [0])))
    _, order = sym_help._topk_helper(g, keeped_scores, elements_num, dim=0)
    indices = g.op('Gather', indices, order, axis_i=0)

    return indices
예제 #3
0
    def reverse(x):
        from torch.onnx.symbolic_opset9 import reshape, transpose, size

        y = transpose(g, x, 0, dim)
        shape = g.op("Shape", y)
        y = reshape(g, y, [0, 1, -1])
        n = size(g, y, g.op("Constant", value_t=torch.LongTensor([0])))
        y = g.op("ReverseSequence", y, n, batch_axis_i=1, time_axis_i=0)
        y = reshape(g, y, shape)
        y = transpose(g, y, 0, dim)
        return y
예제 #4
0
 def symbolic(g,
              features,
              rois,
              out_size,
              spatial_scale,
              sample_num=0,
              aligned=True):
     batch_indices = reshape(
         g,
         g.op('Cast',
              _slice(g, rois, axes=[1], starts=[0], ends=[1]),
              to_i=sym_help.cast_pytorch_to_onnx['Long']), [-1])
     bboxes = _slice(g, rois, axes=[1], starts=[1], ends=[5])
     if aligned:
         scale = sym_help._maybe_get_scalar(spatial_scale)
         offset = g.op("Constant",
                       value_t=torch.tensor(0.5 / scale,
                                            dtype=torch.float32))
         bboxes = sub(g, bboxes, offset)
     out_h, out_w = _pair(out_size)
     return g.op('RoiAlign',
                 features,
                 bboxes,
                 batch_indices,
                 output_height_i=out_h,
                 output_width_i=out_w,
                 sampling_ratio_i=sample_num,
                 spatial_scale_f=spatial_scale)
예제 #5
0
def argmin(g, input, dim, keepdim):
    if sym_help._is_none(dim):
        from torch.onnx.symbolic_opset9 import reshape
        flattened = reshape(g, input, g.op("Constant", value_t=torch.tensor([-1])))
        return g.op("ArgMin", flattened, axis_i=0, keepdims_i=False, select_last_index_i=False)
    else:
        dim = _parse_arg(dim, "i")
        keepdim = _parse_arg(keepdim, "i")
        return g.op("ArgMin", input, axis_i=dim, keepdims_i=keepdim, select_last_index_i=False)
예제 #6
0
def argmin(g, input, dim, keepdim):
    if sym_help._is_none(dim):
        from torch.onnx.symbolic_opset9 import reshape
        flattened = reshape(g, input, (-1,))
        return g.op('ArgMin', flattened, axis_i=0, keepdims_i=False, select_last_index_i=True)
    else:
        dim = _parse_arg(dim, 'i')
        keepdim = _parse_arg(keepdim, 'i')
        return g.op('ArgMin', input, axis_i=dim, keepdims_i=keepdim, select_last_index_i=True)
예제 #7
0
def reshape(g, input, shape):
    if input not in symbolic_helper._quantized_ops:
        return opset9.reshape(g, input, shape)

    kwargs = {
        "Y_scale_f": input.node()["Y_scale"],
        "Y_zero_point_i": input.node()["Y_zero_point"],
    }
    output = g.op("_caffe2::Int8Reshape", input, shape, **kwargs)
    symbolic_helper._quantized_ops.add(output)
    return output
예제 #8
0
def reshape(g, input, shape):
    if input not in sym_help._quantized_ops:
        from torch.onnx.symbolic_opset9 import reshape
        return reshape(g, input, shape)

    kwargs = {
        "Y_scale_f": input.node()["Y_scale"],
        "Y_zero_point_i": input.node()["Y_zero_point"],
    }
    output = g.op("_caffe2::Int8Reshape", input, shape, **kwargs)
    sym_help._quantized_ops.add(output)
    return output
예제 #9
0
 def symbolic(g, features, rois, out_size, spatial_scale, sample_num=0):
     batch_indices = reshape(
         g,
         g.op('Cast',
              _slice(g, rois, axes=[1], starts=[0], ends=[1]),
              to_i=sym_help.cast_pytorch_to_onnx['Long']), [-1])
     bboxes = _slice(g, rois, axes=[1], starts=[1], ends=[5])
     out_h, out_w = _pair(out_size)
     return g.op('RoiAlign',
                 features,
                 bboxes,
                 batch_indices,
                 output_height_i=out_h,
                 output_width_i=out_w,
                 sampling_ratio_i=sample_num,
                 spatial_scale_f=spatial_scale)
def repeat_interleave(g, self, repeats, dim=None):
    from torch.onnx.symbolic_opset9 import reshape
    input = self
    final_dim = dim
    # if dim is None flatten
    # By default, use the flattened input array, and return a flat output array
    if sym_help._is_none(dim):
        input = reshape(g, self, g.op("Constant", value_t=torch.tensor([-1])))
        dim = 0
    else:
        dim = sym_help._maybe_get_scalar(dim)

    repeats_dim = sym_help._get_tensor_rank(repeats)
    repeats_sizes = sym_help._get_tensor_sizes(repeats)
    input_sizes = sym_help._get_tensor_sizes(input)
    if repeats_dim is None:
        raise RuntimeError(
            'Unsupported: ONNX export of repeat_interleave for unknown '
            'repeats rank.')
    if repeats_sizes is None:
        raise RuntimeError(
            'Unsupported: ONNX export of repeat_interleave for unknown '
            'repeats size.')
    if input_sizes is None:
        raise RuntimeError(
            'Unsupported: ONNX export of repeat_interleave for unknown '
            'input size.')
    # Handle cases where dim is negative
    if dim < 0:
        dim += len(input_sizes)

    output_sizes = input_sizes.copy()
    perm_i = [0]
    for idx, input_size in enumerate(input_sizes):
        perm_i.append(idx + 1)
        if input_size is None:
            output_sizes[idx], input_sizes[idx] = 0, -1
    perm_i[0], perm_i[dim] = perm_i[dim], perm_i[0]

    # Cases when repeats is a single value tensor and dim has unknown input size
    if (repeats_dim == 0 or
        (repeats_dim == 1
         and repeats_sizes[0] == 1)) and output_sizes[dim] == 0:
        if not sym_help._is_tensor(repeats):
            repeats = g.op("Constant", value_t=torch.LongTensor(repeats))
        reps = sym_help._size_helper(g, input, dim)
        reps = unsqueeze(g, reps, 0)
        repeats = g.op("Expand", repeats, reps)
    # There are cases when the repeats are 1-d tensor with multiple repeats, but dim
    # provided along one of the dynamic axes provided. A simple example would be
    # input.shape -> [1, 1, *] where * represents the dynamic axes, and dim = 2
    # Now, repeat interleaving can be performed in pytorch when the value of * matches
    # with the number of elements in repeat, for example if * -> 2, number of repeats
    # should be 2 as well.
    else:
        return torch.onnx.symbolic_opset9.repeat_interleave(
            g, self, repeats, final_dim)

    reps_like = g.op("ConstantOfShape",
                     g.op("Shape", repeats),
                     value_t=torch.tensor([1], dtype=torch.long))
    r_splits = split(g, repeats, reps_like, 0)
    i_splits = split(g, input, reps_like, dim)

    output_sizes[dim], input_sizes[dim] = -1, 1

    # Create a loop to iterate over each value along the dimension
    # and perform individual interleaving using the repeats tensor
    # Loop is of the following pattern
    # input (trip_count, cond)
    #   int trip_count = ...;
    #   bool cond = ...;
    #   for (int i=0; i < trip_count && cond; ++i) {
    #     cond = ...;
    #   }

    # Loop conditions
    loop_condition = g.op("Constant", value_t=torch.tensor(1))
    loop_condition = g.op("Cast", loop_condition, to_i=9)
    loop_len = reps
    loop = g.op("Loop", loop_len, loop_condition)

    # Loop inputs
    loop_block = _add_block(loop.node())
    block_input_iter = _add_input_to_block(loop_block)
    cond = _add_input_to_block(loop_block)

    r_split = loop_block.op("SequenceAt", r_splits, block_input_iter)
    i_split = loop_block.op("SequenceAt", i_splits, block_input_iter)

    i_split = unsqueeze(loop_block, i_split, dim + 1)
    r_concat = [
        loop_block.op("Constant",
                      value_t=torch.LongTensor(input_sizes[:dim + 1])),
        r_split,
        loop_block.op("Constant",
                      value_t=torch.LongTensor(input_sizes[dim + 1:]))
    ]
    r_concat = loop_block.op("Concat", *r_concat, axis_i=0)
    i_split = expand(loop_block, i_split, r_concat, None)
    i_split = reshape(loop_block, i_split,
                      g.op("Constant", value_t=torch.LongTensor(output_sizes)))

    # Loop outputs
    cond_out = loop_block.op("Cast", loop_condition, to_i=9)
    _add_output_to_block(loop_block, cond_out)
    _add_output_to_block(loop_block, i_split)
    loop_out = loop.node().output()

    # In this loop, the outputs are scan outputs and are concatenated along
    # the zero'th dimension (by default). In order to avoid this and concatenate
    # along the dimension provided, some post-processing is required
    loop_out = g.op("Transpose", loop_out, perm_i=perm_i)
    return reshape(g, loop_out,
                   g.op("Constant", value_t=torch.LongTensor(output_sizes)))
예제 #11
0
def multiclass_nms_core_symbolic(g,
                                 multi_bboxes,
                                 multi_scores,
                                 score_thr,
                                 nms_cfg,
                                 max_num=-1):

    from torch.onnx.symbolic_opset9 import reshape, squeeze
    from torch.onnx.symbolic_opset10 import _slice

    def cast(x, dtype):
        return g.op('Cast', x, to_i=sym_help.cast_pytorch_to_onnx[dtype])

    def get_size(x, dim):
        shape = g.op('Shape', x)
        dim = _slice(g, shape, axes=[0], starts=[dim], ends=[dim + 1])
        return cast(dim, 'Long')

    nms_op_type = nms_cfg.get('type', 'nms')
    assert nms_op_type == 'nms'
    assert 'iou_thr' in nms_cfg
    iou_threshold = nms_cfg['iou_thr']
    assert 0 <= iou_threshold <= 1

    # Transpose and reshape input tensors to fit ONNX NonMaxSuppression.
    multi_bboxes = reshape(g, multi_bboxes, [0, -1, 4])
    multi_bboxes = g.op('Transpose', multi_bboxes, perm_i=[1, 0, 2])

    batches_num = get_size(multi_bboxes, 0)
    spatial_num = get_size(multi_bboxes, 1)

    multi_scores = g.op('Transpose', multi_scores, perm_i=[1, 0])
    scores_shape = g.op('Concat',
                        batches_num,
                        g.op('Constant', value_t=torch.LongTensor([-1])),
                        spatial_num,
                        axis_i=0)
    multi_scores = reshape(g, multi_scores, scores_shape)
    classes_num = get_size(multi_scores, 1)

    assert max_num > 0

    indices = g.op(
        'NonMaxSuppression', multi_bboxes, multi_scores,
        g.op('Constant', value_t=torch.LongTensor([max_num])),
        g.op('Constant', value_t=torch.FloatTensor([iou_threshold])),
        g.op('Constant', value_t=torch.FloatTensor([score_thr])))

    # Flatten bboxes and scores.
    multi_bboxes_flat = reshape(g, multi_bboxes, [-1, 4])
    multi_scores_flat = reshape(g, multi_scores, [
        -1,
    ])

    # Flatten indices.
    batch_indices = _slice(g, indices, axes=[1], starts=[0], ends=[1])
    class_indices = _slice(g, indices, axes=[1], starts=[1], ends=[2])
    box_indices = _slice(g, indices, axes=[1], starts=[2], ends=[3])

    def add(*args, dtype='Long'):
        x = g.op('Add', args[0], args[1])
        if dtype is not None:
            x = cast(x, dtype)
        return x

    def mul(*args, dtype='Long'):
        x = g.op('Mul', args[0], args[1])
        if dtype is not None:
            x = cast(x, dtype)
        return x

    flat_box_indices = add(mul(batch_indices, spatial_num), box_indices)
    flat_score_indices = add(
        mul(add(mul(batch_indices, classes_num), class_indices), spatial_num),
        box_indices)

    # Select bboxes.
    out_bboxes = reshape(
        g, g.op('Gather', multi_bboxes_flat, flat_box_indices, axis_i=0),
        [-1, 4])
    out_scores = reshape(
        g, g.op('Gather', multi_scores_flat, flat_score_indices, axis_i=0),
        [-1, 1])
    # Having either batch size or number of classes here equal to one is the limitation of implementation.
    class_indices = reshape(g, cast(add(class_indices, batch_indices),
                                    'Float'), [-1, 1])

    # Combine bboxes, scores and labels into a single tensor.
    # This a workaround for a PyTorch bug (feature?),
    # limiting ONNX operations to output only single tensor.
    out_combined_bboxes = g.op('Concat',
                               out_bboxes,
                               out_scores,
                               class_indices,
                               axis_i=1)

    # Get the top scored bboxes only.
    elements_num = sym_help._size_helper(g,
                                         out_scores,
                                         dim=g.op('Constant',
                                                  value_t=torch.LongTensor(
                                                      [0])))
    max_num = g.op('Constant', value_t=torch.LongTensor([max_num]))
    if sym_help._export_onnx_opset_version < 12:
        kn = g.op('Concat', max_num, elements_num, axis_i=0)
        kn = g.op('ReduceMin', kn, keepdims_i=0)
    else:
        kn = g.op('Min', max_num, elements_num)
    _, top_indices = sym_help._topk_helper(g, out_scores, kn, dim=0)
    # top_indices = squeeze(g, top_indices, dim=1)
    top_indices = reshape(g, top_indices, [
        -1,
    ])
    out_combined_bboxes = g.op('Gather',
                               out_combined_bboxes,
                               top_indices,
                               axis_i=0)

    return out_combined_bboxes