def convert_graph(self, source_graph): graph_utils.remove_unreachable(source_graph) target_graph = super(Converter, self).convert_graph( source_graph) # type: NNEFGraph graph_utils.remove_unreachable(target_graph) target_graph.generate_missing_names() return target_graph
def __call__(self, predict_net_path, init_net_path, value_info_path): # type: (str, str, str)->Caffe2Graph net_def = caffe2_pb.NetDef() with open(predict_net_path, 'rb') as f: net_def.ParseFromString(f.read()) predict_graph = get_graph_from_net_def(net_def) net_def = caffe2_pb.NetDef() with open(init_net_path, 'rb') as f: net_def.ParseFromString(f.read()) init_graph = get_graph_from_net_def(net_def) value_info_graph = get_graph_from_value_info( json_utils.load(value_info_path)) combine_graphs_into_predict_graph(predict_graph, init_graph, value_info_graph) tensor_names = {tensor.name for tensor in predict_graph.tensors} for op in list(predict_graph.operations): unify_op(op, tensor_names) caffe2_shapes.infer_shape(op, self.custom_shapes) predict_graph.generate_missing_names() graph_utils.remove_unreachable(predict_graph) return predict_graph
def __call__(self, filename): # type: (str)->ONNXGraph g = read_onnx_from_protobuf(filename) if self._output_names is not None: outputs = [ tensor for tensor in g.tensors if tensor.name in self._output_names ] if len(outputs) != len(self._output_names): found_names = [tensor.name for tensor in outputs] not_found_names = [ output_name for output_name in self._output_names if output_name not in found_names ] raise utils.NNEFToolsException( "Could not find tensor(s) in graph: {}".format( not_found_names)) g.outputs = outputs graph_utils.remove_unreachable(g) if self._infer_shapes: onnx_shape_inference.infer_shapes( g, source_shapes=self._input_shape, custom_shapes=self._custom_shapes) return g
def infer_shapes( graph, # type: ONNXGraph source_shapes=None, # type: typing.Union[typing.Dict[str, typing.List[int]], typing.List[int], int, None] custom_shapes=None, # type: typing.Optional[typing.Dict[str, typing.Callable]] ): # type: (...)->None shape_functions = dict(_DefaultShapes) if custom_shapes: shape_functions.update(custom_shapes) graph.sort() shape_fixer.fix_input_shapes(graph, source_shapes) for op in graph.operations: # Shape prop assert op.name in shape_functions, "No shape function for {}".format( op.name) inferred_shapes, inferred_dtypes = shape_functions[op.name](op) assert not utils.has_le_0(inferred_shapes) assert len(inferred_shapes) == len(inferred_dtypes) == len(op.outputs) for new_shape, new_dtype, tensor in zip(inferred_shapes, inferred_dtypes, op.outputs): assert utils.compatible_shapes(tensor.shape, new_shape) tensor.shape = new_shape assert tensor.dtype is None or tensor.dtype == new_dtype tensor.dtype = new_dtype graph_utils.remove_unreachable(graph)
def convert_graph(self, source_graph): # type: (CaffeGraph)->NNEFGraph caffe_to_nnef_passes.pre_conversion_pass(source_graph) target_graph = super(Converter, self).convert_graph( source_graph) # type: NNEFGraph graph_utils.remove_unreachable(target_graph) target_graph.generate_missing_names() return target_graph
def pre_conversion_transform(g): # type: (TFGraph)->None trafo_by_op_name = { "tf.cast": transform_cast, "tf.fill": transform_fill, "tf.zeros": transform_zeros_ones_like, "tf.ones": transform_zeros_ones_like, "tf.zeros_like": transform_zeros_ones_like, "tf.ones_like": transform_zeros_ones_like, "tf.range": transform_range, "tf.strided_slice": transform_strided_slice, "tf.nn.fused_batch_norm": transform_fused_batch_norm, "_tf.TransposeGrad": transform_transpose_grad, "_tf.strided_slice_grad": transform_strided_slice_grad, "_tf.sqrt_grad": transform_sqrt_grad, "_tf.elu_grad": transform_elu_grad, "_tf.relu_grad": transform_relu_grad, "_tf.relu6_grad": transform_relu6_grad, "_tf.softplus_grad": transform_softplus_grad, "_tf.rsqrt_grad": transform_rsqrt_grad, "_tf.sigmoid_grad": transform_sigmoid_grad, "_tf.tanh_grad": transform_tanh_grad, "_tf.reciprocal_grad": transform_reciprocal_grad, "_tf.bias_add_grad": transform_bias_add_grad, "_tf.MinOrMaxGrad": transform_min_or_max_grad, "_tf.lrn_grad": transform_lrn_grad, } # tf.identity is now not a passthrough, we convert it to copy and the optimizer can remove it passthroughs = ["tf.stop_gradient", "tf.nn.dropout"] tf_py_unify.unify_ops(g) for op in list(g.operations): trafo = trafo_by_op_name.get(op.name) if trafo: trafo(g, op) transform_cgf_stb(g) transform_bts_conv_stb(g) transform_pad(g) transform_add_conv(g) transform_bias_add_conv(g) graph_utils.remove_passthroughs( g, is_passthrough=lambda op_: op_.name in passthroughs) graph_utils.remove_unreachable(g) transform_separate_inputs_and_outputs(g) transform_separate_duplicated_outputs(g) g.generate_missing_names()
def convert(tf_graph, enable_default_conversion=False): # type: (TFGraph, bool)->None tf_graph.sort() transform_fuse_bias_add_to_conv(tf_graph) transform_fuse_add_to_matmul(tf_graph) transform_fuse_activations(tf_graph) for tensor in tf_graph.tensors: tensor.dtype = _to_tflite_dtype(tensor.dtype) for op in list(tf_graph.operations): # Conversion _DefaultConverters.get(op.name, convert_custom)(op) graph_utils.remove_unreachable(tf_graph) tf_graph.generate_missing_names()
def __call__(self, filename): g = read_tf_graph_from_protobuf(filename) if self._output_names is not None: outputs = [tensor for tensor in g.tensors if tensor.name in self._output_names] if len(outputs) != len(self._output_names): found_names = [tensor.name for tensor in outputs] not_found_names = [output_name for output_name in self._output_names if output_name not in found_names] raise utils.NNEFToolsException("Could not find tensor(s) in graph: {}".format(not_found_names)) g.outputs = outputs graph_utils.remove_unreachable(g) unsupported_ops = set(op.name for op in g.operations if op.name not in tf_pb_to_tf_py.DefaultConverters) if unsupported_ops: raise utils.NNEFToolsException("Unsupported operation(s): {}".format(unsupported_ops)) if self._convert_to_tf_py: tf_pb_to_tf_py.evaluate_and_convert(g, source_shapes=self._input_shape) return g
def convert(tf_graph, enable_default_conversion=False): # type: (TFGraph, bool)->None tf_graph.sort() transform_fuse_bias_add_to_conv(tf_graph) transform_fuse_add_to_matmul(tf_graph) transform_fuse_activations(tf_graph) for tensor in tf_graph.tensors: tensor.dtype = _to_tflite_dtype(tensor.dtype) for op in list(tf_graph.operations): # Conversion assert enable_default_conversion or op.name in _DefaultConverters, \ "No tf_py_to_tflite converter for {}".format(op.name) if op.name in _DefaultConverters: _DefaultConverters[op.name](op) graph_utils.remove_unreachable(tf_graph) tf_graph.generate_missing_names()
def propagate(graph, source_shapes=None): # type: (ONNXGraph, typing.Union[typing.Dict[str, typing.List[int]], typing.List[int], int, None])->None graph.sort() shape_fixer.fix_input_shapes(graph, source_shapes) for op in graph.operations: # Shape prop assert op.name in _DefaultPropagators, "No shape propagator for {}".format( op.name) propagated_shapes, propagated_dtypes = _DefaultPropagators[op.name](op) assert not utils.has_le_0(propagated_shapes) assert len(propagated_shapes) == len(propagated_dtypes) == len( op.outputs) for new_shape, new_dtype, tensor in zip(propagated_shapes, propagated_dtypes, op.outputs): assert utils.compatible_shapes(tensor.shape, new_shape) tensor.shape = new_shape assert tensor.dtype is None or tensor.dtype == new_dtype tensor.dtype = new_dtype graph_utils.remove_unreachable(graph)
def convert(tf_graph, enable_default_conversion=False): # type: (TFGraph, bool)->None tf_graph.sort() for tensor in tf_graph.tensors: if tensor.name is not None and ':' not in tensor.name: # The tf-to-nnef converter distinguishes original and generated tensors based on the presence of the ':' tensor.name = tensor.name + ":0" tensor.dtype = _to_tf_py_dtype(tensor.dtype) for op in list(tf_graph.operations): # Conversion assert enable_default_conversion or op.name in _DefaultConverters, \ "No tflite_to_tf_py converter for {}".format(op.name) act_fun = op.attribs.get('fused_activation_function', None) if act_fun == 'NONE': act_fun = None output = op.output if act_fun else None if op.name in _DefaultConverters: _DefaultConverters[op.name](op) if act_fun: assert act_fun in ["RELU", "RELU6"] last_new_op = output.producer last_new_op.outputs = (TFTensor(graph=op.graph, name=None, shape=list(output.shape), dtype=output.dtype), ) TFOperation(graph=last_new_op.graph, name=_to_tf_py_activation_function(act_fun), inputs=last_new_op.output, outputs=output) graph_utils.remove_unreachable(tf_graph)
def post_conversion_pass(g): # type: (NNEFGraph)->None graph_utils.remove_unreachable(g) # _small_variables_to_consts(g) _merge_pads(g) graph_utils.remove_unreachable(g)
def pre_conversion_pass(g): # type: (CaffeGraph)->None graph_utils.remove_unreachable(g) graph_utils.remove_passthroughs(g, is_passthrough=lambda op: op.name in ('Dropout', 'Silence')) _merge_batch_norm_and_scale(g) graph_utils.remove_unreachable(g)
def post_conversion_pass(g): # type: (CaffeGraph)->None graph_utils.remove_unreachable(g) _unite_powers(g) _merge_up_bias(g) graph_utils.remove_unreachable(g)
def pre_conversion_pass(g): # type: (NNEFGraph)->None graph_utils.remove_unreachable(g) _create_thresholds(g) _create_elus(g) graph_utils.remove_unreachable(g)
def trace( network_function, # type: typing.Callable[[], typing.Any] checkpoint_path=None, # type: typing.Optional[str] raise_on_missing_weight=True, # type: bool expand_gradients=False, # type: bool custom_traceable_functions=None # type: typing.Optional[typing.List[TraceableFunction]] ): # type: (...)->TFGraph if custom_traceable_functions is None: custom_traceable_functions = [] traceable_functions = DefaultTraceableFunctions + custom_traceable_functions for trf in traceable_functions: trf.eval_functions() functions_by_name = { trf.op_proto.op_name: trf.functions for trf in traceable_functions } if expand_gradients: del functions_by_name["tf.gradients"] tracer = _InvocationTracer(functions_by_name) result = tracer.trace(network_function, allow_nesting=expand_gradients) result = _eliminate_named_tuples(result) invocations = tracer.invocations for invocation in invocations: invocation.args = _eliminate_named_tuples(invocation.args) invocation.result = _eliminate_named_tuples(invocation.result) invocations = _eliminate_identities(invocations) # _print_invocations(invocations) if expand_gradients: _fix_strange_grad_functions(invocations) invocations = _eliminate_nesting(invocations) # _print_invocations(invocations) assert not _check_has_untraced_ops(invocations, result), \ "There were untraced operations. " \ "Add the untraced operations to custom_functions_to_trace or use only supported operations." op_proto_by_name = { trf.op_proto.op_name: trf.op_proto for trf in traceable_functions } tf_graph = _to_tf_graph(network_function.__name__, invocations, result, op_proto_by_name) graph_utils.remove_unreachable(tf_graph) if checkpoint_path: checkpoint_reader = tf.contrib.framework.load_checkpoint( checkpoint_path) for tensor in tf_graph.list_variables(): assert tensor.name.endswith( "/read:0"), "Strange variable name: {}".format(tensor.name) var_name = tensor.name[:-len("/read:0")] if checkpoint_reader.has_tensor(var_name): tensor.data = checkpoint_reader.get_tensor(var_name) if not isinstance(tensor.data, np.ndarray): tensor.data = np.array(tensor.data).reshape(tensor.shape) elif raise_on_missing_weight: assert False, "Checkpoint {} does not have var {}".format( checkpoint_path, var_name) return tf_graph
def transform_remove_inverse_transposes( g, # type: BaseGraph transforms_by_name, # type:typing.Dict[str, typing.List[Transform]] merge_into_constants, # type: bool merge_into_variables, # type: bool driver, # type: DataFormatOptimizationDriver transposable_ops=None, # type: typing.Optional[typing.List[TransposableOperation]] ): # type: (...)-> None if transposable_ops is None: transposable_ops = [] transposable_op_by_name = { } # type: typing.Dict[str, TransposableOperation] transposable_op_by_name.update({top.name: top for top in transposable_ops}) for op in g.operations: if op.name == driver.transpose_op_name and op.output.rank > len( driver.get_axes_from_transpose(op)): driver.set_axes_on_transpose( op, driver.get_axes_from_transpose(op) + list(range( op.output.rank))[len(driver.get_axes_from_transpose(op)):]) matches = _find_inverse_transposes( g, transposable_op_names=set(six.iterkeys(transposable_op_by_name)), merge_into_constants=merge_into_constants, merge_into_variables=merge_into_variables, driver=driver) for axes, subgraph in matches: upper_perm = axes if subgraph.started_down else utils.inverse_permutation( axes) lower_perm = utils.inverse_permutation(upper_perm) upper_boundary = [ be for be in subgraph.boundary_elements if not be.from_up ] lower_boundary = [ be for be in subgraph.boundary_elements if be.from_up ] for _, tensor in upper_boundary: if tensor.producer is not None and tensor.producer.name == driver.transpose_op_name: if tensor in g.outputs: graph_output = driver.create_tensor( graph=g, name=tensor.name, shape=utils.apply_permutation( tensor.producer.input.shape, upper_perm), dtype=tensor.producer.input.dtype) driver.create_transpose_op(graph=g, input=tensor.producer.input, axes=list(upper_perm), output=graph_output) graph_utils.replace_tensor_in_outputs( g, tensor, graph_output) elif (len(tensor.producer.input.consumers) == 1 and tensor.producer.input not in g.inputs and tensor.producer.input not in g.outputs): tensor.producer.input.name = tensor.name add_transform(transforms_by_name, tensor.producer.input, Transpose(lower_perm)) remove_passthrough_ex(g, tensor.producer) else: assert (merge_into_variables and tensor.is_variable) \ or (merge_into_constants and tensor.is_constant) apply_transpose_to_varlike(tensor, lower_perm, transforms_by_name) skipped_ops = set( tensor.producer for tensor in subgraph.skipped_tensors) # type: typing.Set[BaseOperation] for op in skipped_ops: assert op.name in transposable_op_by_name transposable_op_by_name[op.name].dg_transpose( _transposer, g, op, lower_perm) for output in op.outputs: if output in g.outputs: graph_output = driver.create_tensor(graph=g, name=output.name, shape=output.shape, dtype=output.dtype) driver.create_transpose_op(graph=g, input=output, axes=list(upper_perm), output=graph_output) graph_utils.replace_tensor_in_outputs( g, output, graph_output) output.name = None output.shape = utils.apply_permutation( output.shape, lower_perm) else: output.shape = utils.apply_permutation( output.shape, lower_perm) add_transform(transforms_by_name, output, Transpose(lower_perm)) for _, tensor in lower_boundary: if tensor.producer is not None and tensor.producer.name == driver.transpose_op_name: if tensor in g.outputs: graph_output = driver.create_tensor( graph=g, name=tensor.name, shape=tensor.producer.input.shape, dtype=tensor.producer.input.dtype) driver.create_copy_op(graph=g, input=tensor.producer.input, output=graph_output) graph_utils.replace_tensor_in_outputs( g, tensor, graph_output) remove_passthrough_ex(g, tensor.producer) elif tensor.producer is not None and tensor.producer.name == driver.squeeze_op_name: driver.set_axes_on_squeeze( tensor.producer, sorted( Transposer.apply_permutation_to_axes( driver.get_axes_from_squeeze(tensor.producer), lower_perm))) else: assert False graph_utils.remove_unreachable(g)
def combine_graphs_into_predict_graph(predict_graph, init_graph, value_info_graph): # type: (Caffe2Graph, Caffe2Graph, Caffe2Graph)->None input_names = {input.name for input in value_info_graph.inputs} predict_tensor_by_name = { tensor.name: tensor for tensor in predict_graph.tensors } in_init_but_not_in_predict = [] for init_tensor in init_graph.tensors: if init_tensor.name not in input_names: if not init_tensor.producer: raise ReadException( "Tensor '{}' does not have a producer in the init-net". format(init_tensor.name)) init_op = init_tensor.producer if init_op.name not in Caffe2DTypeByInitializer: raise ReadException("Initializer '{}' is not supported".format( init_op.name)) if init_tensor.name not in predict_tensor_by_name: in_init_but_not_in_predict.append(init_tensor.name) continue predict_tensor = predict_tensor_by_name[ init_tensor.name] # type: Caffe2Tensor assert predict_tensor.producer is None and predict_tensor.shape is None predict_tensor.shape = list(init_op.attribs['shape']) predict_tensor.dtype = Caffe2DTypeByInitializer[init_op.name] predict_tensor.data = np.reshape(init_op.attribs['values'], init_op.attribs['shape']) if init_op.name in ('Int8GivenTensorFill', 'Int8GivenIntTensorFill'): predict_tensor.quantization = Caffe2Quantization( init_op.attribs['Y_scale'], init_op.attribs['Y_zero_point']) input_tensors = [] for value_info_tensor in sorted(value_info_graph.tensors, key=lambda t: t.name): if value_info_tensor.name not in predict_tensor_by_name: possible_input_names = ' or '.join( sorted([ '"' + t.name + '"' for t in predict_graph.tensors if t.producer is None and ( t.data is None or t.data.size <= 1) ])) raise ReadException( "Tensor '{}' is in value-info but not in predict-net.\n" "Possible input tensors: {}".format(value_info_tensor.name, possible_input_names)) input_tensor = predict_tensor_by_name[value_info_tensor.name] assert input_tensor.producer is None and input_tensor.shape is None and input_tensor.data is None input_tensor.shape = value_info_tensor.shape input_tensor.dtype = value_info_tensor.dtype input_tensors.append(input_tensor) predict_graph.inputs = input_tensors if not predict_graph.outputs: predict_graph.outputs = [ t for t in predict_graph.tensors if not t.consumers ] graph_utils.remove_unreachable(predict_graph) for tensor in predict_graph.tensors: if (not tensor.producers and not tensor.is_variable and not tensor.is_constant and tensor not in predict_graph.inputs): raise ReadException( "Tensor '{}' has no initializer but is not listed as input in value-info." .format(tensor.name)) if in_init_but_not_in_predict: print( "Warning: There were tensors in the init-net that are not present in the predict-net: {}" .format(in_init_but_not_in_predict)) for tensor in predict_graph.tensors: if tensor.data is not None and tensor.data.size == 0: print( "Warning: Tensor '{}' possibly missing from value_info.json (has zero size)" .format(tensor.name))