From 4939a4a5303f0c6acd2c1f35761a9971539af2b3 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=EA=B9=80=EC=9A=A9=EC=84=AD/=EB=8F=99=EC=9E=91=EC=A0=9C?= =?utf8?q?=EC=96=B4Lab=28SR=29/Engineer/=EC=82=BC=EC=84=B1=EC=A0=84?= =?utf8?q?=EC=9E=90?= Date: Thu, 5 Jul 2018 13:06:20 +0900 Subject: [PATCH] Introduce OperatorParser for model_parser (#1858) * Introduce OperatorParser for model_parser - To remove `TFLiteModelFileParser`'s dependencies for tflite.operator, tflite.tensor, introduce new class `OperatorParser`. - `OperatorParser` class handles `Operator` class and `Tensor` class. - Not yet changed model_parser.py. To test this, it needs another commit for new operator counter and changes on model_parser.py Signed-off-by: Yongseop Kim * Rename __ to _wrapping --- tools/tflitefile_tool/operator_parser.py | 118 +++++++++++++++++++++++++++++ tools/tflitefile_tool/operator_wrapping.py | 68 +++++++++++++++++ tools/tflitefile_tool/tensor_wrapping.py | 50 ++++++++++++ 3 files changed, 236 insertions(+) create mode 100755 tools/tflitefile_tool/operator_parser.py create mode 100755 tools/tflitefile_tool/operator_wrapping.py create mode 100755 tools/tflitefile_tool/tensor_wrapping.py diff --git a/tools/tflitefile_tool/operator_parser.py b/tools/tflitefile_tool/operator_parser.py new file mode 100755 index 0000000..94365ef --- /dev/null +++ b/tools/tflitefile_tool/operator_parser.py @@ -0,0 +1,118 @@ +#!/usr/bin/python + +import tflite.Model +import tflite.SubGraph +import tflite.Operator +import tflite.OperatorCode +import tflite.BuiltinOperator +from operator_wrapping import Operator, SetBuiltinOpcodeStr, BuiltinOpcodeStrList +from tensor_wrapping import Tensor, SetTensorTypeStr +from operator_counter import OperationCount +import perf_predictor + + +class TypesCounter(object): + def __init__(self): + self.type_count = 0 + self.op_count = OperationCount() + + def Update(self, op): + self.type_count = self.type_count + 1 + self.op_count.Increase(op.GetOpCount()) + + def GetTypeCount(self): + return self.type_count + + def GetOpCount(self): + return self.op_count + + +class OperatorParser(object): + def __init__(self, tf_model, tf_subgraph, perf_predictor=None): + self.tf_model = tf_model + self.tf_subgraph = tf_subgraph + self.perf_predictor = perf_predictor + self.operators = list() + self.op_types = dict() + # Built-in operator string table + SetBuiltinOpcodeStr() + # Tensor type string table + SetTensorTypeStr() + + def Parse(self): + for operator_idx in range(self.tf_subgraph.OperatorsLength()): + tf_operator = self.tf_subgraph.Operators(operator_idx) + opcode_str = self.GetOpcodeStr(tf_operator) + input_tensors = self.GetInputTensors(tf_operator) + output_tensors = self.GetOutputTensors(tf_operator) + + op = Operator(operator_idx, tf_operator, input_tensors, output_tensors, + opcode_str) + op.CountOperations() + self.operators.append(op) + + self.CountOperator(op) + + def GetOpcodeStr(self, tf_operator): + opcode_list_idx = tf_operator.OpcodeIndex() + opcode_id = self.tf_model.OperatorCodes(opcode_list_idx).BuiltinCode() + opcode_str = BuiltinOpcodeStrList[opcode_id] + if opcode_id == 32: + # Custom operator + custom_operator = self.tf_model.OperatorCodes(tf_operator.OpcodeIndex()) + custom_op_name = custom_operator.CustomCode().decode('utf-8') + opcode_str = opcode_str + "(" + custom_op_name + ")" + return opcode_str + + def CountOperator(self, op): + opcode_str = op.GetOpcodeStr() + if opcode_str not in self.op_types: + self.op_types[opcode_str] = TypesCounter() + self.op_types[opcode_str].Update(op) + + def GetInputTensors(self, tf_operator): + operator_inputs = tf_operator.InputsAsNumpy() + return self.GetTensors(operator_inputs) + + def GetOutputTensors(self, tf_operator): + operator_outputs = tf_operator.OutputsAsNumpy() + return self.GetTensors(operator_outputs) + + def GetTensors(self, tf_tensors_index): + return_list = list() + for tensor_idx in tf_tensors_index: + tf_tensor = self.tf_subgraph.Tensors(tensor_idx) + buffer_idx = tf_tensor.Buffer() + tf_buffer = self.tf_model.Buffers(buffer_idx) + return_list.append(Tensor(tensor_idx, tf_tensor, tf_buffer)) + return return_list + + def PrintAll(self): + self.PrintAllOperators() + print('') + self.PrintAllOperatorTypes() + print('') + + def PrintAllOperators(self): + for operator in self.operators: + operator.PrintInfo(self.perf_predictor) + print('') + + def PrintAllOperatorTypes(self): + print("Number of operator types: {0}".format(len(self.op_types))) + + total_opstr_count = 0 + total_op_count = 0 + for opstr, type_counter in self.op_types.items(): + opstr_count = type_counter.GetTypeCount() + op_count = type_counter.GetOpCount().TotalCount() + + print("\t{0:38}: {1:4} \t (total_ops: {2})".format(opstr, opstr_count, + "{:,}".format(op_count))) + + total_opstr_count = total_opstr_count + opstr_count + total_op_count = total_op_count + op_count + + print("{0:46}: {1:4} \t (total_ops: {2})".format("Total Number of operators", + total_opstr_count, + "{:,}".format(total_op_count))) diff --git a/tools/tflitefile_tool/operator_wrapping.py b/tools/tflitefile_tool/operator_wrapping.py new file mode 100755 index 0000000..c498325 --- /dev/null +++ b/tools/tflitefile_tool/operator_wrapping.py @@ -0,0 +1,68 @@ +#!/usr/bin/python + +import tflite.Operator +import tflite.OperatorCode +import tflite.BuiltinOperator +from tensor_wrapping import Tensor +from operator_counter import OperationCount, ops_counters +from perf_predictor import PerfPredictor + +BuiltinOpcodeStrList = {} + + +# Match enum value integer to name string +# Assumption 1: enum value is defined by old style (can be used on python 2) +# Assumption 2: when class define enum value, only constant value is defined and methods are not defined +# Assumption 3: only integer value is set by constant definition +def SetBuiltinOpcodeStr(): + builtinOpObj = tflite.BuiltinOperator.BuiltinOperator() + + for fieldName in dir(builtinOpObj): + if (not fieldName.startswith('_')): + fieldValue = getattr(builtinOpObj, fieldName) + if (isinstance(fieldValue, (int))): + BuiltinOpcodeStrList[fieldValue] = fieldName + + +def GetStrTensorIndex(tensors): + return_string = "[" + for idx in range(len(tensors)): + if idx != 0: + return_string += ", " + return_string += str(tensors[idx].tensor_idx) + return_string += "]" + return return_string + + +class Operator(object): + def __init__(self, operator_idx, tf_operator, input_tensors, output_tensors, + opcode_str): + self.operator_idx = operator_idx + self.tf_operator = tf_operator + self.inputs = input_tensors + self.outputs = output_tensors + self.opcode_str = opcode_str + self.op_count = None # OperationCount + + def PrintInfo(self, perf_predictor=None): + cycles = (perf_predictor.PredictCycles( + self.op_count)) if perf_predictor != None else "???" + print("Operator {0}: {1} (ops: {2}, cycls: {3})".format( + self.operator_idx, self.opcode_str, "{:,}".format(self.op_count.TotalCount()), + "{:,}".format(cycles))) + print("\tInput Tensors" + GetStrTensorIndex(self.inputs)) + for tensor in self.inputs: + tensor.PrintInfo("\t\t") + print("\tOutput Tensors" + GetStrTensorIndex(self.outputs)) + for tensor in self.outputs: + tensor.PrintInfo("\t\t") + + def CountOperations(self): + self.op_count = ops_counters[self.opcode_str](self.tf_operator, self.inputs, + self.outputs) + + def GetOpcodeStr(self): + return self.opcode_str + + def GetOpCount(self): + return self.op_count diff --git a/tools/tflitefile_tool/tensor_wrapping.py b/tools/tflitefile_tool/tensor_wrapping.py new file mode 100755 index 0000000..34d75d3 --- /dev/null +++ b/tools/tflitefile_tool/tensor_wrapping.py @@ -0,0 +1,50 @@ +#!/usr/bin/python + +import tflite.Tensor +import tflite.TensorType + +TensorTypeList = {} + + +def SetTensorTypeStr(): + tensorTypeObj = tflite.TensorType.TensorType() + + for fieldName in dir(tensorTypeObj): + if (not fieldName.startswith('_')): + fieldValue = getattr(tensorTypeObj, fieldName) + if (isinstance(fieldValue, (int))): + TensorTypeList[fieldValue] = fieldName + + +class Tensor(object): + def __init__(self, tensor_idx, tf_tensor, tf_buffer): + self.tensor_idx = tensor_idx + self.tf_tensor = tf_tensor + self.tf_buffer = tf_buffer + + def PrintInfo(self, depth_str=""): + buffer_idx = self.tf_tensor.Buffer() + isEmpty = "Filled" + if (self.tf_buffer.DataLength() == 0): + isEmpty = " Empty" + shape_str = self.GetShapeString() + type_name = TensorTypeList[self.tf_tensor.Type()] + + shape_name = "" + if self.tf_tensor.Name() != 0: + shape_name = self.tf_tensor.Name() + + print_str = "Tensor {0:4} : buffer {1:4} | {2} | {3:7} | Shape {4} ({5})".format( + self.tensor_idx, buffer_idx, isEmpty, type_name, shape_str, shape_name) + print(depth_str + print_str) + + def GetShapeString(self): + if self.tf_tensor.ShapeLength() == 0: + return "Scalar" + return_string = "[" + for shape_idx in range(self.tf_tensor.ShapeLength()): + if (shape_idx != 0): + return_string += ", " + return_string += str(self.tf_tensor.Shape(shape_idx)) + return_string += "]" + return return_string -- 2.7.4