Ejemplo n.º 1
0
    def parse(self):
        logger.debug("Parsing %s...", self.type)
        op = self.tflite
        opcode = self.model.OperatorCodes(op.OpcodeIndex()).BuiltinCode()
        assert (opcode in self.TypeMapping)

        assert (op.InputsLength() == 3), "TFLite Conv always has bias"
        assert (op.OutputsLength() == 1)

        # input
        ilayout = Layout('NHWC', 'NCHW')
        it = self.parseInput(0, ilayout)

        # weight
        wlayout = Layout('CHWM', 'MCHW') if self.isDepthwise else Layout(
            'OHWI', 'OIHW')
        wt = self.parseInput(1, wlayout)

        # bias
        self.parseInput(2, is_bias=True)

        # output
        olayout = Layout('NHWC', 'NCHW')
        ot = self.parseOutput(0, olayout)

        # options
        op_opt = op.BuiltinOptions()
        option = tflite.DepthwiseConv2DOptions(
        ) if self.isDepthwise else tflite.Conv2DOptions()
        option.Init(op_opt.Bytes, op_opt.Pos)

        self.attrs['dilations'] = [
            option.DilationHFactor(),
            option.DilationWFactor()
        ]
        self.attrs['group'] = wt.shape[3] if self.isDepthwise else 1
        self.attrs['kernel_shape'] = wt.shape[1:3]
        self.attrs['strides'] = [option.StrideH(), option.StrideW()]
        # XXX Not enabled as ONNXRuntime has limitation to infer pads for non-1 dilation
        # self.attrs['auto_pad'] = PaddingMapping[option.Padding()]
        if self.isDepthwise:
            assert (option.DepthMultiplier() == 1)
        self.attrs['pads'] = computePaddingSize(option.Padding(),
                                                it.shape[1:3],
                                                self.attrs['kernel_shape'],
                                                self.attrs['strides'],
                                                self.attrs['dilations'])

        handleFusedActivation(self, option, ot)

        self.setParsed()
Ejemplo n.º 2
0
def handle_conv2d_parsing(op: tflite.Operator, builtin_code: tflite.BuiltinOperator, graph: tflite.SubGraph, model: tflite.Model) -> Tuple[str, Layer]:
    op_opt = op.BuiltinOptions()

    opt = tflite.Conv2DOptions()
    opt.Init(op_opt.Bytes, op_opt.Pos)


    stride = opt.StrideW()

    input_ids = op.InputsAsNumpy()
    output_ids = op.OutputsAsNumpy()
    assert len(input_ids) == 3
    input_tensor_id, filter_tensor_id, bias_tensor_id = list(input_ids)

    input_tensor = graph.Tensors(input_tensor_id)
    input_shape = input_tensor.ShapeAsNumpy()
    assert len(input_shape) == 4
    _, input_width, input_height, input_depth = list(input_shape)

    filter_tensor = graph.Tensors(filter_tensor_id)
    filter_shape = filter_tensor.ShapeAsNumpy()
    assert len(filter_shape) == 4
    if builtin_code == tflite.BuiltinOperator.CONV_2D:
        filter_count, filter_width, filter_height, filter_in_channels = list(filter_shape)
    else:
        filter_in_channels, filter_width, filter_height, filter_count = list(filter_shape)
    filter_quantization = filter_tensor.Quantization()
    filter_scales = filter_quantization.ScaleAsNumpy()

    filter_data = clean_data_int_op(model.Buffers(filter_tensor.Buffer()).DataAsNumpy(), is_32=False).reshape(filter_shape)

    padding = opt.Padding()
    padding_size = 0
    if padding == tflite.Padding.SAME:
        padding_size = int(filter_width) // 2
    else:
        # PADDING should be valid in this case
        raise Exception(f"Unexpected padding type: {padding}")

    bias_tensor = graph.Tensors(bias_tensor_id)
    bias_data = clean_data_int_op(model.Buffers(bias_tensor.Buffer()).DataAsNumpy(), is_32=True)

    assert len(output_ids) == 1
    output_id = output_ids[0]
    output_tensor = graph.Tensors(output_id)
    output_shape = output_tensor.ShapeAsNumpy()
    output_batch_size, output_width, output_height, output_channels = list(output_shape)
    assert output_channels == filter_count

    output_quantization = output_tensor.Quantization().ScaleAsNumpy()
    assert len(output_quantization) == 1
    output_scale = output_quantization[0]

    activation_type = opt.FusedActivationFunction()
    followed_by_relu = activation_type == tflite.ActivationFunctionType.RELU

    output_name = output_tensor.Name()
    input_name = input_tensor.Name()

    if builtin_code == tflite.BuiltinOperator.CONV_2D:
        return output_name, Conv2d(
            input=None,
            filter_width=filter_width, filter_height=filter_height, filter_in_channels=filter_in_channels, filter_count=filter_count, filter_data=filter_data, bias_data=bias_data,
            input_width=input_width, input_height=input_height, stride=stride, padding=padding_size,
            followed_by_relu=followed_by_relu, output_scale=output_scale, filter_scales=filter_scales,
            output_width=output_width, output_height=output_height
        ), input_name
    else:
        return output_name, Conv2d_DW(
            input=None,
            filter_width=filter_width, filter_height=filter_height, filter_in_channels=filter_in_channels, filter_count=filter_count, filter_data=filter_data, bias_data=bias_data,
            input_width=input_width, input_height=input_height, stride=stride, padding=padding_size,
            followed_by_relu=followed_by_relu, output_scale=output_scale, filter_scales=filter_scales,
            output_width=output_width, output_height=output_height
        ), input_name
Ejemplo n.º 3
0
def test_mobilenet():
    cur_dir = os.path.dirname(os.path.abspath(__file__))
    tflm_dir = os.path.abspath(cur_dir + '/../assets/tests')
    tflm_name = 'mobilenet_v1_1.0_224_quant.tflite'
    path = os.path.join(tflm_dir, tflm_name)
    with open(path, 'rb') as f:
        buf = f.read()
        model = tflite.Model.GetRootAsModel(buf, 0)

    ############# model #########################################################

    # Version of the TFLite Converter.
    assert (model.Version() == 3)

    # Strings are binary format, need to decode.
    # Description is useful when exchanging models.
    assert (model.Description().decode('utf-8') == 'TOCO Converted.')

    # How many operator types in this model.
    assert (model.OperatorCodesLength() == 5)

    # A model may have multiple subgraphs.
    assert (model.SubgraphsLength() == 1)

    # How many tensor buffer.
    assert (model.BuffersLength() == 90)

    ############# subgraph ######################################################

    # Chose one subgraph.
    graph = model.Subgraphs(0)

    # Tensors in the subgraph are represented by index description.
    assert (graph.InputsLength() == 1)
    assert (graph.OutputsLength() == 1)
    assert (graph.InputsAsNumpy()[0] == 88)
    assert (graph.OutputsAsNumpy()[0] == 87)
    # All arrays can dump as Numpy array, or access individually.
    assert (graph.Inputs(0) == 88)
    assert (graph.Outputs(0) == 87)

    # Name may used to debug or check for model containing multiple subgraphs.
    assert (graph.Name() == None)

    # Operators in the subgraph.
    assert (graph.OperatorsLength() == 31)

    # Let's use the first operator.
    op = graph.Operators(0)

    ############# operator type #################################################

    # Operator Type is also stored as index, which can obtain from `Model` object.
    op_code = model.OperatorCodes(op.OpcodeIndex())

    # The first operator is a convolution.
    assert (op_code.BuiltinCode() == tflite.BuiltinOperator.CONV_2D)

    # Custom operator need more interface, won't cover here.
    assert (op_code.BuiltinCode() != tflite.BuiltinOperator.CUSTOM)

    ############# the operator ##################################################

    # The first operator is a convolution.

    # The inputs are: data, weight and bias.
    assert (op.InputsLength() == 3)
    assert (op.OutputsLength() == 1)

    # The data of first Conv2D is input of the model
    assert (op.Inputs(0) == 88)
    assert (op.Inputs(0) == graph.Inputs(0))

    # Operators have dedicated options per its type
    assert (op.BuiltinOptionsType() == tflite.BuiltinOptions.Conv2DOptions)
    op_opt = op.BuiltinOptions()

    ############# operator option ###############################################

    # Check the Conv2D options.

    # Parse the Table of options.
    opt = tflite.Conv2DOptions()
    opt.Init(op_opt.Bytes, op_opt.Pos)

    # The options.
    assert (opt.Padding() == tflite.Padding.SAME)
    assert (opt.StrideW() == 2)
    assert (opt.StrideH() == 2)
    assert (opt.DilationWFactor() == 1)
    assert (opt.DilationHFactor() == 1)

    # Further check activation function type if there were.
    assert (
        opt.FusedActivationFunction() == tflite.ActivationFunctionType.NONE)

    ############# tensor ########################################################

    # Check the weight tensor of the first convolution.
    tensor_index = op.Inputs(1)

    # use `graph.Tensors(index)` to get the tensor object.
    tensor = graph.Tensors(tensor_index)

    # view the shape
    assert (tensor.ShapeLength() == 4)
    assert (tensor.ShapeAsNumpy()[1] == 3)
    # All arrays can dump as Numpy array, or access individually.
    assert (tensor.Shape(1) == 3)

    # data type has been encoded
    assert (tensor.Type() == tflite.TensorType.UINT8)

    # name is in binary format, decode it
    assert (
        tensor.Name().decode('utf-8') ==
        'MobilenetV1/MobilenetV1/Conv2d_0/weights_quant/FakeQuantWithMinMaxVars'
    )

    # buffer of the tensor is represented in index too.
    assert (tensor.Buffer() == 66)

    # quantization parameters of the tensor, only valid for quantized model
    assert (tensor.Quantization())

    assert (not tensor.IsVariable())

    ############# quant #######################################################

    # Quantization parameters of the tensor, only valid for quantized model
    quant = tensor.Quantization()

    # Scale and zero point
    assert (quant.ScaleAsNumpy()[0] == 0.02182667888700962)
    assert (quant.ZeroPointAsNumpy()[0] == 151)

    # Min/max also avaiable
    assert (quant.MinAsNumpy()[0] == -3.265998125076294)
    assert (quant.MaxAsNumpy()[0] == 2.2779781818389893)

    # All arrays can dump as Numpy array, or access individually.
    assert (quant.Scale(0) == 0.02182667888700962)
    assert (quant.ZeroPoint(0) == 151)
    assert (quant.Min(0) == -3.265998125076294)
    assert (quant.Max(0) == 2.2779781818389893)

    ############# memory #######################################################

    # Get the buffer object.
    buf = model.Buffers(tensor.Buffer())

    assert (buf.DataLength() == 864)
    assert (buf.DataAsNumpy()[0] == 151)
    # All arrays can dump as Numpy array, or access individually.
    assert (buf.Data(0) == 151)
    # The Numpy array is flattened.
    npa = buf.DataAsNumpy()
    assert (npa.shape == (864, ))
Ejemplo n.º 4
0
assert (op.OutputsLength() == 1)

# The data of first Conv2D is input of the model
assert (op.Inputs(0) == 88)
assert (op.Inputs(0) == graph.Inputs(0))

# Operators have dedicated options per its type
assert (op.BuiltinOptionsType() == tflite.BuiltinOptions.Conv2DOptions)
op_opt = op.BuiltinOptions()

############# operator option ###############################################

# Check the Conv2D options.

# Parse the Table of options.
opt = tflite.Conv2DOptions()
opt.Init(op_opt.Bytes, op_opt.Pos)

# The options.
assert (opt.Padding() == tflite.Padding.SAME)
assert (opt.StrideW() == 2)
assert (opt.StrideH() == 2)
assert (opt.DilationWFactor() == 1)
assert (opt.DilationHFactor() == 1)

# Further check activation function type if there were.
assert (opt.FusedActivationFunction() == tflite.ActivationFunctionType.NONE)

############# tensor ########################################################

# Check the weight tensor of the first convolution.
Ejemplo n.º 5
0
def calc_flops(path):
    with open(path, 'rb') as f:
        buf = f.read()
        model = tflite.Model.GetRootAsModel(buf, 0)

    graph = model.Subgraphs(0)

    help(tflite.BuiltinOperator)
    # ABS = 101
    # CONV_2D = 3
    # CUMSUM = 128

    # print funcs
    _dict_builtin_op_code_to_name = {
        v: k
        for k, v in tflite.BuiltinOperator.__dict__.items() if type(v) == int
    }

    def print_header():
        print("%-18s | M FLOPS" % ("OP_NAME"))
        print("------------------------------")

    def print_flops(op_code_builtin, flops):
        print("%-18s | %.1f" %
              (_dict_builtin_op_code_to_name[op_code_builtin], flops / 1.0e6))

    def print_none(op_code_builtin):
        print("%-18s | <IGNORED>" %
              (_dict_builtin_op_code_to_name[op_code_builtin]))

    def print_footer(total_flops):
        print("------------------------------")
        print("Total: %.1f M FLOPS" % (total_flops / 1.0e6))

    total_flops = 0.0
    print_header()
    for i in range(graph.OperatorsLength()):
        op = graph.Operators(i)
        op_code = model.OperatorCodes(op.OpcodeIndex())
        op_code_builtin = op_code.BuiltinCode()

        op_opt = op.BuiltinOptions()

        flops = 0.0
        if op_code_builtin == tflite.BuiltinOperator.CONV_2D:
            # input shapes: in, weight, bias
            in_shape = graph.Tensors(op.Inputs(0)).ShapeAsNumpy()
            filter_shape = graph.Tensors(op.Inputs(1)).ShapeAsNumpy()
            bias_shape = graph.Tensors(op.Inputs(2)).ShapeAsNumpy()
            # output shape
            out_shape = graph.Tensors(op.Outputs(0)).ShapeAsNumpy()
            # ops options
            opt = tflite.Conv2DOptions()
            opt.Init(op_opt.Bytes, op_opt.Pos)
            # opt.StrideH()

            # flops. 2x means mul(1)+add(1). 2x not needed if you calculate MACCs
            # refer to https://github.com/AlexeyAB/darknet/src/convolutional_layer.c `l.blopfs =`
            flops = 2 * out_shape[1] * out_shape[2] * filter_shape[
                0] * filter_shape[1] * filter_shape[2] * filter_shape[3]
            print_flops(op_code_builtin, flops)

        elif op_code_builtin == tflite.BuiltinOperator.DEPTHWISE_CONV_2D:
            in_shape = graph.Tensors(op.Inputs(0)).ShapeAsNumpy()
            filter_shape = graph.Tensors(op.Inputs(1)).ShapeAsNumpy()
            out_shape = graph.Tensors(op.Outputs(0)).ShapeAsNumpy()
            # flops
            flops = 2 * out_shape[1] * out_shape[2] * filter_shape[
                0] * filter_shape[1] * filter_shape[2] * filter_shape[3]
            print_flops(op_code_builtin, flops)

        else:
            print_none(op_code_builtin)

        total_flops += flops
    print_footer(total_flops)