1010from ...models .neural_network .update_optimizer_utils import SgdParams
1111from ...proto import FeatureTypes_pb2 as _FeatureTypes_pb2
1212from collections import OrderedDict as _OrderedDict
13+ from ...proto import Model_pb2 as _Model_pb2
1314from ...models import datatypes
1415from ...models import MLModel as _MLModel
1516from ...models .utils import save_spec as _save_spec
3233
3334 _keras .layers .convolutional .Conv2D : _layers2 .convert_convolution ,
3435 _keras .layers .convolutional .Conv2DTranspose : _layers2 .convert_convolution ,
35- _keras .layers .convolutional .SeparableConv2D : _layers2 .convert_separable_convolution ,
36+ _keras .layers .convolutional .SeparableConv2D : _layers2 .convert_separable_convolution ,
3637 _keras .layers .pooling .AveragePooling2D : _layers2 .convert_pooling ,
3738 _keras .layers .pooling .MaxPooling2D : _layers2 .convert_pooling ,
3839 _keras .layers .pooling .GlobalAveragePooling2D : _layers2 .convert_pooling ,
6364 _keras .layers .Maximum : _layers2 .convert_merge ,
6465 _keras .layers .Concatenate : _layers2 .convert_merge ,
6566 _keras .layers .Dot : _layers2 .convert_merge ,
66-
67+
6768 _keras .layers .core .Flatten : _layers2 .convert_flatten ,
6869 _keras .layers .core .Permute :_layers2 .convert_permute ,
6970 _keras .layers .core .Reshape :_layers2 .convert_reshape ,
9192 _KERAS_LAYER_REGISTRY [_keras .engine .topology .InputLayer ] = \
9293 _layers2 .default_skip
9394 # end if _HAS_KERAS2_TF
94-
95+
9596
9697def _is_merge_layer (layer ):
9798 if _HAS_KERAS2_TF :
@@ -106,7 +107,7 @@ def _is_activation_layer(layer):
106107 isinstance (layer , _keras .layers .advanced_activations .LeakyReLU ) or
107108 isinstance (layer , _keras .layers .advanced_activations .PReLU ) or
108109 isinstance (layer , _keras .layers .advanced_activations .ELU ) or
109- isinstance (layer ,
110+ isinstance (layer ,
110111 _keras .layers .advanced_activations .ThresholdedReLU ) or
111112 isinstance (layer , _keras .layers .advanced_activations .Softmax ))
112113
@@ -163,7 +164,7 @@ def _load_keras_model(model_network_path, model_weight_path, custom_objects=None
163164 Path where the model network weights are (hd5 file)
164165
165166 custom_objects:
166- A dictionary of layers or other custom classes
167+ A dictionary of layers or other custom classes
167168 or functions used by the model
168169
169170 Returns
@@ -300,7 +301,8 @@ def _convert(model,
300301 custom_objects = None ,
301302 input_shapes = None ,
302303 output_shapes = None ,
303- respect_trainable = False ):
304+ respect_trainable = False ,
305+ use_float_arraytype = False ):
304306
305307 # Check Keras format
306308 if _keras .backend .image_data_format () == 'channels_first' :
@@ -309,48 +311,48 @@ def _convert(model,
309311 "Changing to 'channels_last', but your model may not be converted "
310312 "converted properly." )
311313 _keras .backend .set_image_data_format ('channels_last' )
312-
314+
313315 # Check custom conversion functions / custom objects
314316 add_custom_layers = custom_conversion_functions is not None
315317
316318 if isinstance (model , _string_types ):
317319 model = _keras .models .load_model (model , custom_objects = custom_objects )
318320 elif isinstance (model , tuple ):
319321 model = _load_keras_model (model [0 ], model [1 ])
320-
322+
321323 # Check valid versions
322324 _check_unsupported_layers (model , add_custom_layers )
323-
325+
324326 # Build network graph to represent Keras model
325327 graph = _topology2 .NetGraph (model )
326328 graph .build ()
327329
328330 # The graph should be finalized before executing this
329331 graph .generate_blob_names ()
330332 graph .add_recurrent_optionals ()
331-
333+
332334 inputs = graph .get_input_layers ()
333335 outputs = graph .get_output_layers ()
334-
336+
335337 # check input / output names validity
336- if input_names is not None :
338+ if input_names is not None :
337339 if isinstance (input_names , _string_types ):
338340 input_names = [input_names ]
339- else :
341+ else :
340342 input_names = ['input' + str (i + 1 ) for i in range (len (inputs ))]
341343
342- if output_names is not None :
344+ if output_names is not None :
343345 if isinstance (output_names , _string_types ):
344346 output_names = [output_names ]
345- else :
347+ else :
346348 output_names = ['output' + str (i + 1 ) for i in range (len (outputs ))]
347-
349+
348350 if image_input_names is not None and isinstance (image_input_names , _string_types ):
349351 image_input_names = [image_input_names ]
350-
352+
351353 graph .reset_model_input_names (input_names )
352354 graph .reset_model_output_names (output_names )
353-
355+
354356 # Keras -> Core ML input dimension dictionary
355357 # (None, None) -> [1, 1, 1, 1, 1]
356358 # (None, D) -> [D] or [D, 1, 1, 1, 1]
@@ -478,7 +480,8 @@ def _convert(model,
478480 input_features = list (zip (input_names , input_types ))
479481 output_features = list (zip (output_names , output_types ))
480482
481- builder = _NeuralNetworkBuilder (input_features , output_features , mode = mode )
483+ builder = _NeuralNetworkBuilder (input_features , output_features , mode = mode ,
484+ use_float_arraytype = use_float_arraytype )
482485
483486 for iter , layer in enumerate (graph .layer_list ):
484487 keras_layer = graph .keras_layer_map [layer ]
@@ -548,4 +551,21 @@ def _convert(model,
548551
549552 # Return the protobuf spec
550553 spec = builder .spec
554+
555+ # If the model has multi-arrays of type double, recommend to the user the utility function
556+ # coremltools.models.utils.convert_double_to_float_multiarray_type(spec)
557+ has_double_multiarray = False
558+ for feature in list (spec .description .input ) + list (spec .description .output ):
559+ if feature .type .HasField ('multiArrayType' ):
560+ if feature .type .multiArrayType .dataType == _Model_pb2 .ArrayFeatureType .DOUBLE :
561+ has_double_multiarray = True
562+ break
563+
564+ if has_double_multiarray :
565+ print ("\n \n Recommendation: This model has at least one multiarray input/output of type double.\n "
566+ "For large sized arrays, multiarrays of type float32 are more efficient.\n "
567+ "In future, float input/output multiarrays will be produced by default by the converter.\n "
568+ "Please use, either the flag 'use_float_arraytype' during the call to convert or\n "
569+ "the utility 'coremltools.utils.convert_double_to_float_multiarray_type(spec)', post-conversion.\n \n " )
570+
551571 return spec
0 commit comments