code: def _convert(
      model,
      input_names=None,
      output_names=None,
      image_input_names=None,
      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="",
      custom_objects=None,
      respect_trainable=False,
  ):
      if not (_HAS_KERAS_TF):
          raise RuntimeError(
              "keras not found or unsupported version or backend "
              "found. keras conversion API is disabled."
          )
      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], custom_objects=custom_objects
          )
  
      # Check valid versions
      _check_unsupported_layers(model)
  
      # Build network graph to represent Keras model
      graph = _topology.NetGraph(model)
      graph.build()
      graph.remove_skip_layers(_KERAS_SKIP_LAYERS)
      graph.insert_1d_permute_layers()
      graph.insert_permute_for_spatial_bn()
      graph.defuse_activation()
      graph.remove_internal_input_layers()
      graph.make_output_layers()
  
      # 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, 1, D, 1]
      # (Batch, Sequence, D) -> [D]
  
      # Retrieve input shapes from model
      if type(model.input_shape) is list:
          input_dims = [
              list(filter(None, x)) for x in model.input_shape
          ]
          unfiltered_shapes = model.input_shape
      else:
          input_dims = [list(filter(None, model.input_shape))]
          unfiltered_shapes = [model.input_shape]
  
      for idx, dim in enumerate(input_dims):
          unfiltered_shape = unfiltered_shapes[idx]
          if len(dim) == 0:
              # Used to be [None, None] before filtering; indicating unknown
              # sequence length
              input_dims[idx] = tuple([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
          elif len(dim) == 2:  # [Seq, D]
              input_dims[idx] = (dim[1],)
          elif len(dim) == 3:  # H,W,C
              if len(unfiltered_shape) > 3:
                  # keras uses the reverse notation from us
                  input_dims[idx] = (dim[2], dim[0], dim[1])
              else:  # keras provided fixed batch and sequence length, so the input
                  # was (batch, sequence, channel)
                  input_dims[idx] = (dim[2],)
          else:
              raise ValueError(
                  "Input"
                  + input_names[idx]
                  + "has input shape of length"
                  + str(len(dim))
              )
  
      # Retrieve output shapes from model
      if type(model.output_shape) is list:
          output_dims = [
              list(filter(None, x)) for x in model.output_shape
          ]
      else:
          output_dims = [list(filter(None, model.output_shape[1:]))]
  
      for idx, dim in enumerate(output_dims):
          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[1], dim[0])
  
      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
      )
  
      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)
          input_names, output_names = graph.get_layer_blobs(layer)
          converter_func(
              builder, layer, input_names, output_names, keras_layer
          )
  
      # Set the right inputs and outputs on the model description (interface)
      builder.set_input(input_names, input_dims)
      builder.set_output(output_names, output_dims)
  
      # 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 paramsters
      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,
      )
  
      # Return the protobuf spec
      spec = builder.spec
      return spec
  
file_path: turicreate/src/external/coremltools_wrap/coremltools/coremltools/converters/keras/_keras_converter.py
imports: from six import string_types
  from ...models.neural_network import NeuralNetworkBuilder
  from ...proto import FeatureTypes_pb2
  from collections import OrderedDict
  from ...models import datatypes, _METADATA_VERSION, _METADATA_SOURCE
  from ...models import MLModel
  from ...models import _MLMODEL_FULL_PRECISION, _MLMODEL_HALF_PRECISION, _VALID_MLMODEL_PRECISION_TYPES
  from ...models.utils import _convert_neural_network_spec_weights_to_fp16
  from ..._deps import _HAS_KERAS_TF
  from ..._deps import _HAS_KERAS2_TF
  from coremltools import __version__
label_desc: Use image node_id 4 for calling a global function with example usage: _convert(model, input_names, output_names, image_input_names, is_bgr, red_bias, green_bias, blue_bias, gray_bias, image_scale, class_labels, predicted_feature_name, predicted_probabilities_output, custom_objects, respect_trainable) and returns: spec