class_docstr: Class to convert MXNet to ONNX graph
class_docstr_tok: ["Class","to","convert","MXNet","to","ONNX","graph"]
docstr: Convert MXNet graph to ONNX graph
  
  Parameters
  ----------
  sym : :class:`~mxnet.symbol.Symbol`
      MXNet symbol object
  params : dict of ``str`` to :class:`~mxnet.ndarray.NDArray`
      Dict of converted parameters stored in ``mxnet.ndarray.NDArray`` format
  in_shapes : List of tuple
      Input shape of the model e.g [(1,3,224,224)]
  in_types : List of Int
      Input ONNX data types
  verbose : Boolean
      If true will print logs of the model conversion
  opset_version : Int
      ONNX opset version to use for export, defaults to latest supported by onnx package
  dynamic: Boolean
      If True will allow for dynamic input shapes to the model
  dynamic_input_shapes: list of tuple
      Specifies the dynamic input_shapes. If None then all dimensions are set to None
  
  Returns
  -------
  graph : GraphProto
      ONNX graph
docstr_tok: ["Convert","MXNet","graph","to","ONNX","graph","Parameters","--","--","--","--","--","sym",":",":","class",":","`","~mxnet.symbol.Symbol","`","MXNet","symbol","object","params",":","dict","of","``","str","``","to",":","class",":","`","~mxnet.ndarray.NDArray","`","Dict","of","converted","parameters","stored","in","``","mxnet.ndarray.NDArray","``","format","in_shapes",":","List","of","tuple","Input","shape","of","the","model","e.g","[","(","1,3,224,224",")","]","in_types",":","List","of","Int","Input","ONNX","data","types","verbose",":","Boolean","If","true","will","print","logs","of","the","model","conversion","opset_version",":","Int","ONNX","opset","version","to","use","for","export",",","defaults","to","latest","supported","by","onnx","package","dynamic",":","Boolean","If","True","will","allow","for","dynamic","input","shapes","to","the","model","dynamic_input_shapes",":","list","of","tuple","Specifies","the","dynamic","input_shapes",".","If","None","then","all","dimensions","are","set","to","None","Returns","--","--","--","-","graph",":","GraphProto","ONNX","graph"]
code: def create_onnx_graph_proto(
      self,
      sym,
      params,
      in_shapes,
      in_types,
      verbose=False,
      opset_version=None,
      dynamic=True,
      dynamic_input_shapes=None,
  ):
      """Convert MXNet graph to ONNX graph
  
      Parameters
      ----------
      sym : :class:`~mxnet.symbol.Symbol`
          MXNet symbol object
      params : dict of ``str`` to :class:`~mxnet.ndarray.NDArray`
          Dict of converted parameters stored in ``mxnet.ndarray.NDArray`` format
      in_shapes : List of tuple
          Input shape of the model e.g [(1,3,224,224)]
      in_types : List of Int
          Input ONNX data types
      verbose : Boolean
          If true will print logs of the model conversion
      opset_version : Int
          ONNX opset version to use for export, defaults to latest supported by onnx package
      dynamic: Boolean
          If True will allow for dynamic input shapes to the model
      dynamic_input_shapes: list of tuple
          Specifies the dynamic input_shapes. If None then all dimensions are set to None
  
      Returns
      -------
      graph : GraphProto
          ONNX graph
      """
      try:
          from onnx import (
              helper,
              NodeProto,
              ValueInfoProto,
              TensorProto,
          )
          from onnx.helper import make_tensor_value_info
          from onnx.defs import onnx_opset_version
      except ImportError:
          raise ImportError(
              "Onnx and protobuf need to be installed. "
              + "Instructions to install - https://github.com/onnx/onnx"
          )
  
      if opset_version is None:
          opset_version = onnx_opset_version()
  
      # When MXNet model is saved to json file , MXNet adds a node for label.
      # The name of this node is, name of the last node + "_label" ( i.e if last node
      # name is "Softmax", this node will have a name "Softmax_label". Also, the new node
      # will always be second last node in the json graph.
      # Deriving the output_label name.
      output_label = (
          sym.get_internals()[len(sym.get_internals()) - 1].name
          + "_label"
      )
  
      weights = MXNetGraph.convert_weights_to_numpy(params)
  
      mx_graph = json.loads(sym.tojson())["nodes"]
  
      class NodeOutput:
          def __init__(self, name, dtype):
              self.name = name
              self.dtype = np.dtype(dtype)
  
      initializer = []
      all_processed_nodes = []
      onnx_processed_nodes = []
      onnx_processed_inputs = []
      onnx_processed_outputs = []
      outputs_lookup = []
  
      # Determine graph output names, shapes, and dtypes. Also update in_shapes
      in_shapes, graph_outputs = MXNetGraph.get_outputs(
          sym,
          params,
          in_shapes,
          output_label,
          in_types,
          dynamic,
          dynamic_input_shapes,
      )
      appeared_names = set()
      graph_input_idx = 0
      for idx, node in enumerate(mx_graph):
          op = node["op"]
          # check if the current node has the same name as nodes before
          if node["name"] in appeared_names:
              node["name"] = "idx_" + str(idx) + "_" + node["name"]
          else:
              appeared_names.add(node["name"])
          name = node["name"]
          if verbose:
              logging.info(
                  "Converting idx: %d, op: %s, name: %s", idx, op, name
              )
  
          # A node is an input node if its op_name is "null" and is not
          # in params dict
          if op == "null" and name not in params:
              # Handle graph input
  
              # Skip output_label node, as this node is not part of graph
              # Refer to "output_label" assignment above for more details.
              if name == output_label:
                  continue
  
              converted, dtypes = MXNetGraph.convert_layer(
                  node,
                  is_input=True,
                  mx_graph=mx_graph,
                  weights=weights,
                  in_shape=in_shapes[graph_input_idx],
                  in_type=in_types[graph_input_idx],
                  proc_nodes=all_processed_nodes,
                  initializer=initializer,
                  outputs_lookup=outputs_lookup,
              )
              graph_input_idx += 1
          else:
              # Handle graph layers
              converted, dtypes = MXNetGraph.convert_layer(
                  node,
                  is_input=False,
                  mx_graph=mx_graph,
                  weights=weights,
                  proc_nodes=all_processed_nodes,
                  initializer=initializer,
                  outputs_lookup=outputs_lookup,
                  idx=idx,
                  opset_version=opset_version,
              )
          if isinstance(converted, list):
              # Collect all the node's output names
              node_possible_names = [name] + [
                  name + str(i) for i in range(100)
              ]
              node_output_names = []
              # Collect all the graph's output names
              graph_output_names = []
              # Iterate for all converted nodes
              for converted_node in converted:
                  # If converted node is ValueInfoProto, add it in inputs
                  if isinstance(converted_node, ValueInfoProto):
                      onnx_processed_inputs.append(converted_node)
                  # If converted node is NodeProto, add it in processed nodes list
                  elif isinstance(converted_node, NodeProto):
                      onnx_processed_nodes.append(converted_node)
                      # some operators have multiple outputs,
                      # therefore, check all output node names
                      node_names = list(converted_node.output)
                      for nodename in node_names:
                          if nodename in node_possible_names:
                              node_output_names.append(nodename)
                          if nodename in graph_outputs:
                              graph_output_names.append(nodename)
                              if verbose:
                                  logging.info(
                                      "Output node is: {}".format(
                                          nodename
                                      )
                                  )
                  elif isinstance(converted_node, TensorProto):
                      raise ValueError("Did not expect TensorProto")
                  else:
                      raise ValueError(
                          f"node is of an unrecognized type: {type(node)}"
                      )
  
                  all_processed_nodes.append(converted_node)
  
              # if node_output_names is empty then we use the last returned node as output
              if not node_output_names:
                  node_output_names = [converted[-1].name]
  
              # process node outputs (sort by output index)
              def str2int(s, l):
                  if len(s) == l:
                      return -1
                  else:
                      return int(s[l:])
  
              node_output_names = sorted(
                  node_output_names, key=lambda x: str2int(x, len(name))
              )
  
              # match the output names to output dtypes
              if dtypes is not None:
                  assert len(node_output_names) == len(dtypes)
                  node_outputs = [
                      NodeOutput(node_output_names[i], dtypes[i])
                      for i in range(len(dtypes))
                  ]
              else:
                  # in case dtypes is None, we just default to the dtype of the first input
                  assert len(node["inputs"]) > 0
                  first_input = node["inputs"][0]
                  first_input_dtype = outputs_lookup[first_input[0]][
                      first_input[1]
                  ].dtype
                  node_outputs = [
                      NodeOutput(n, first_input_dtype)
                      for n in node_output_names
                  ]
              outputs_lookup.append(node_outputs)
  
              # process graph outputs (sort by alphabetical order)
              graph_output_names.sort()
              for nodename in graph_output_names:
                  onnx_processed_outputs.append(
                      make_tensor_value_info(
                          name=nodename,
                          elem_type=graph_outputs[nodename]["dtype"],
                          shape=graph_outputs[nodename]["shape"],
                      )
                  )
  
          else:
              logging.info(
                  "Operator converter function should always return a list"
              )
  
      # sometimes the graph output can also be in the intializer
      for i in initializer:
          if i.name in graph_outputs:
              onnx_processed_outputs.append(
                  make_tensor_value_info(
                      name=i.name,
                      elem_type=graph_outputs[i.name]["dtype"],
                      shape=graph_outputs[i.name]["shape"],
                  )
              )
  
      graph = helper.make_graph(
          onnx_processed_nodes,
          "mxnet_converted_model",
          onnx_processed_inputs,
          onnx_processed_outputs,
      )
  
      graph.initializer.extend(initializer)
  
      return graph
  
file_path: mxnet/python/mxnet/onnx/mx2onnx/_export_onnx.py
imports: import logging
  import json
  import numpy
  from mxnet import ndarray
label_desc: Use image node_id 7 for calling the MXNetGraph obj's underlying member method code with example usage: obj.create_onnx_graph_proto(sym, params, in_shapes, in_types, verbose, opset_version, dynamic, dynamic_input_shapes) and returns: graph, unknown, int