code: def _convert(
      model,
      input_names=None,
      output_names=None,
      image_input_names=None,
      input_name_shape_dict={},
      is_bgr=False,
      red_bias=0.0,
      green_bias=0.0,
      blue_bias=0.0,
      gray_bias=0.0,
      image_scale=1.0,
      class_labels=None,
      predicted_feature_name=None,
      predicted_probabilities_output="",
      add_custom_layers=False,
      custom_conversion_functions=None,
      custom_objects=None,
      input_shapes=None,
      output_shapes=None,
      respect_trainable=False,
      use_float_arraytype=False,
  ):
      # Check Keras format
      if _keras.backend.image_data_format() == "channels_first":
          print(
              "Keras image data format 'channels_first' detected. Currently "
              "only 'channels_last' is supported. "
              "Changing to 'channels_last', but your model may not be converted "
              "converted properly."
          )
          _keras.backend.set_image_data_format("channels_last")
  
      # Check custom conversion functions / custom objects
      add_custom_layers = custom_conversion_functions is not None
  
      if isinstance(model, _string_types):
          model = _keras.models.load_model(
              model, custom_objects=custom_objects
          )
      elif isinstance(model, tuple):
          model = _load_keras_model(model[0], model[1])
  
      # Check valid versions
      _check_unsupported_layers(model, add_custom_layers)
  
      # Build network graph to represent Keras model
      graph = _topology2.NetGraph(model)
      graph.build()
  
      # The graph should be finalized before executing this
      graph.generate_blob_names()
      graph.add_recurrent_optionals()
  
      inputs = graph.get_input_layers()
      outputs = graph.get_output_layers()
  
      # check input / output names validity
      if input_names is not None:
          if isinstance(input_names, _string_types):
              input_names = [input_names]
      else:
          input_names = [
              "input" + str(i + 1) for i in range(len(inputs))
          ]
  
      if output_names is not None:
          if isinstance(output_names, _string_types):
              output_names = [output_names]
      else:
          output_names = [
              "output" + str(i + 1) for i in range(len(outputs))
          ]
  
      if image_input_names is not None and isinstance(
          image_input_names, _string_types
      ):
          image_input_names = [image_input_names]
  
      graph.reset_model_input_names(input_names)
      graph.reset_model_output_names(output_names)
  
      # Keras -> Core ML input dimension dictionary
      # (None, None) -> [1, 1, 1, 1, 1]
      # (None, D) -> [D] or [D, 1, 1, 1, 1]
      # (None, Seq, D) -> [Seq, 1, D, 1, 1]
      # (None, H, W, C) -> [C, H, W]
      # (D) -> [D]
      # (Seq, D) -> [Seq, 1, D, 1, 1]
      # (Batch, Sequence, D) -> [D]
      # (Batch, Seq, H, W, C) -> (C,H,W)
  
      # Retrieve input shapes from model
      if len(model._inbound_nodes) > 1 and input_shapes is not None:
          input_dims = [filter(None, x) for x in input_shapes]
          unfiltered_shapes = input_shapes
      else:
          if type(model.input_shape) is list:
              input_dims = [filter(None, x) for x in model.input_shape]
              unfiltered_shapes = model.input_shape
          else:
              input_dims = [filter(None, model.input_shape)]
              unfiltered_shapes = [model.input_shape]
  
      for idx, dim in enumerate(input_dims):
          if input_names[idx] in input_name_shape_dict:
              unfiltered_shape = input_name_shape_dict[input_names[idx]]
              dim = list(filter(None, unfiltered_shape))
          else:
              unfiltered_shape = unfiltered_shapes[idx]
              dim = list(input_dims[idx])
  
          if len(unfiltered_shape) == 1:
              if len(dim) == 1:
                  input_dims[idx] = dim  # dim is just a number
              else:
                  errMsg = "Invalid input shape for '{}'.\n".format(
                      input_names[idx]
                  )
                  errMsg += "Please provide a finite channel value (D) using input_name_shape_dict arg "
                  errMsg += "with key = '{}' and value = [D]".format(
                      input_names[idx]
                  )
                  raise ValueError(errMsg)
  
          elif len(unfiltered_shape) == 2:
              if len(dim) == 2:  # [Seq, D]
                  input_dims[idx] = (dim[1],)
              elif len(dim) == 1:
                  s = graph.get_successors(inputs[idx])[0]
                  if isinstance(
                      graph.get_keras_layer(s),
                      _keras.layers.embeddings.Embedding,
                  ):
                      # Embedding layer's special input (None, D) where D is
                      # actually sequence length
                      input_dims[idx] = (1,)
                  else:
                      input_dims[idx] = dim  # dim is just a number
              else:  # Used to be [None, None] before filtering; indicating unknown
                  # sequence length
                  input_dims[idx] = tuple([1])
  
          elif len(unfiltered_shape) == 3:
              if (
                  len(dim) == 3
              ):  # keras provided fixed batch and sequence length,
                  # so the input was (batch, sequence, channel)
                  input_dims[idx] = (dim[2],)
              elif len(dim) == 2:  # [None, Seq, D]
                  input_dims[idx] = (dim[1],)
              elif len(dim) == 1:
                  input_dims[idx] = dim  # dim is just a number
              else:
                  errMsg = "Invalid input shape for '{}'.\n".format(
                      input_names[idx]
                  )
                  errMsg += (
                      "Please provide a finite channel value (D) using "
                  )
                  errMsg += (
                      "input_name_shape_dict arg with key = '{}' and "
                      "value = [None, None, D]".format(input_names[idx])
                  )
                  raise ValueError(errMsg)
  
          elif len(unfiltered_shape) == 4:
              if (
                  len(dim) == 3
              ):  # keras uses the reverse notation from CoreML
                  input_dims[idx] = (dim[2], dim[0], dim[1])
              else:
                  errMsg = "Invalid input shape for '{}'.\n".format(
                      input_names[idx]
                  )
                  errMsg += (
                      "Please provide a finite height (H), width (W) & "
                      "channel value (C) "
                  )
                  errMsg += (
                      "using input_name_shape_dict arg with key = '{}' "
                      "and value = [None, H, W, C]\n".format(
                          input_names[idx]
                      )
                  )
                  errMsg += (
                      "Converted .mlmodel can be modified to have flexible "
                      "input shape using coremltools.models.neural_network.flexible_shape_utils"
                  )
                  raise ValueError(errMsg)
  
          elif len(unfiltered_shape) == 5:
              if (
                  len(dim) == 4
              ):  # keras uses the reverse notation from CoreML
                  input_dims[idx] = (dim[-1], dim[-3], dim[-2])
              else:
                  errMsg = "Invalid input shape for '{}', shape:{}.\n".format(
                      input_names[idx], str(unfiltered_shape)
                  )
                  raise ValueError(errMsg)
          else:
              raise ValueError(
                  "Input '%s' has input shape of length %d"
                  % (input_names[idx], len(dim))
              )
  
      # Retrieve output shapes from model
      if len(model._outbound_nodes) > 1 and output_shapes is not None:
          output_dims = [filter(None, x) for x in output_shapes]
      else:
          if type(model.output_shape) is list:
              output_dims = [
                  filter(None, x) for x in model.output_shape
              ]
          else:
              output_dims = [filter(None, model.output_shape[1:])]
  
      for idx, dim in enumerate(output_dims):
          dim = list(dim)
          if len(dim) == 1:
              output_dims[idx] = dim
          elif len(dim) == 2:  # [Seq, D]
              output_dims[idx] = (dim[1],)
          elif len(dim) == 3:
              output_dims[idx] = (dim[2], dim[0], dim[1])
  
      input_types = [datatypes.Array(*dim) for dim in input_dims]
      output_types = [datatypes.Array(*dim) for dim in output_dims]
  
      # Some of the feature handling is sensitive about string vs. unicode
      input_names = map(str, input_names)
      output_names = map(str, output_names)
      is_classifier = class_labels is not None
      if is_classifier:
          mode = "classifier"
      else:
          mode = None
  
      # assuming these match
      input_features = list(zip(input_names, input_types))
      output_features = list(zip(output_names, output_types))
  
      builder = _NeuralNetworkBuilder(
          input_features,
          output_features,
          mode=mode,
          use_float_arraytype=use_float_arraytype,
      )
  
      for iter, layer in enumerate(graph.layer_list):
          keras_layer = graph.keras_layer_map[layer]
          print("%d : %s, %s" % (iter, layer, keras_layer))
          if isinstance(
              keras_layer, _keras.layers.wrappers.TimeDistributed
          ):
              keras_layer = keras_layer.layer
          converter_func = _get_layer_converter_fn(
              keras_layer, add_custom_layers
          )
          input_names, output_names = graph.get_layer_blobs(layer)
          # this may be none if we're using custom layers
          if converter_func:
              converter_func(
                  builder,
                  layer,
                  input_names,
                  output_names,
                  keras_layer,
                  respect_trainable,
              )
          else:
              if _is_activation_layer(keras_layer):
                  import six
  
                  if six.PY2:
                      layer_name = keras_layer.activation.func_name
                  else:
                      layer_name = keras_layer.activation.__name__
              else:
                  layer_name = type(keras_layer).__name__
              if layer_name in custom_conversion_functions:
                  custom_spec = custom_conversion_functions[layer_name](
                      keras_layer
                  )
              else:
                  custom_spec = None
  
              builder.add_custom(
                  layer, input_names, output_names, custom_spec
              )
  
      # Since we aren't mangling anything the user gave us, we only need to update
      # the model interface here
      builder.add_optionals(
          graph.optional_inputs, graph.optional_outputs
      )
  
      # Add classifier classes (if applicable)
      if is_classifier:
          classes_in = class_labels
          if isinstance(classes_in, _string_types):
              import os
  
              if not os.path.isfile(classes_in):
                  raise ValueError(
                      "Path to class labels (%s) does not exist."
                      % classes_in
                  )
              with open(classes_in, "r") as f:
                  classes = f.read()
              classes = classes.splitlines()
          elif type(classes_in) is list:  # list[int or str]
              classes = classes_in
          else:
              raise ValueError(
                  "Class labels must be a list of integers / strings, or a file path"
              )
  
          if predicted_feature_name is not None:
              builder.set_class_labels(
                  classes,
                  predicted_feature_name=predicted_feature_name,
                  prediction_blob=predicted_probabilities_output,
              )
          else:
              builder.set_class_labels(classes)
  
      # Set pre-processing parameters
      builder.set_pre_processing_parameters(
          image_input_names=image_input_names,
          is_bgr=is_bgr,
          red_bias=red_bias,
          green_bias=green_bias,
          blue_bias=blue_bias,
          gray_bias=gray_bias,
          image_scale=image_scale,
      )
  
      # add in the loss and optimizer, if the network has it and that is
      # appropriate given the flag.
      if respect_trainable:
          _convert_training_info(model, builder, output_features)
  
      # Return the protobuf spec
      spec = builder.spec
  
      # If the model has multi-arrays of type double, recommend to the user the utility function
      # coremltools.models.utils.convert_double_to_float_multiarray_type(spec)
      has_double_multiarray = False
      for feature in list(spec.description.input) + list(
          spec.description.output
      ):
          if feature.type.HasField("multiArrayType"):
              if (
                  feature.type.multiArrayType.dataType
                  == _Model_pb2.ArrayFeatureType.DOUBLE
              ):
                  has_double_multiarray = True
                  break
  
      if has_double_multiarray:
          print(
              "\n\nRecommendation: This model has at least one multiarray input/output of type double.\n"
              "For large sized arrays, multiarrays of type float32 are more efficient.\n"
              "In future, float input/output multiarrays will be produced by default by the converter.\n"
              "Please use, either the flag 'use_float_arraytype' during the call to convert or\n"
              "the utility 'coremltools.utils.convert_double_to_float_multiarray_type(spec)', post-conversion.\n\n"
          )
  
      return spec
  
file_path: turicreate/src/external/coremltools_wrap/coremltools/coremltools/converters/keras/_keras2_converter.py
imports: import logging
  from six import string_types
  from ...models.neural_network import NeuralNetworkBuilder
  from ...models.neural_network.update_optimizer_utils import AdamParams
  from ...models.neural_network.update_optimizer_utils import SgdParams
  from ...proto import FeatureTypes_pb2
  from collections import OrderedDict
  from ...proto import Model_pb2
  from ...models import datatypes
  from ...models import MLModel
  from ...models.utils import save_spec
  from ..._deps import _HAS_KERAS2_TF
label_desc: Use image node_id 7 for calling a global function with example usage: _convert(model, input_names, output_names, image_input_names, input_name_shape_dict, is_bgr, red_bias, green_bias, blue_bias, gray_bias, image_scale, class_labels, predicted_feature_name, predicted_probabilities_output, add_custom_layers, custom_conversion_functions, custom_objects, input_shapes, output_shapes, respect_trainable, use_float_arraytype) and returns: spec