def _test_keras_model(self, model, data_mode='random', delta=1e-2, use_cpu_only=True, has_variables=True): """Saves out the backend TF graph from the Keras model and tests it """ model_dir = tempfile.mkdtemp() graph_def_file = os.path.join(model_dir, 'tf_graph.pb') checkpoint_file = os.path.join(model_dir, 'tf_model.ckpt') frozen_model_file = os.path.join(model_dir, 'tf_frozen.pb') coreml_model_file = os.path.join(model_dir, 'coreml_model.mlmodel') input_shapes = { inp.op.name: inp.shape.as_list() for inp in model.inputs } for name, shape in input_shapes.items(): input_shapes[name] = [ dim if dim is not None else 1 for dim in shape ] output_node_names = [output.op.name for output in model.outputs] tf_graph = K.get_session().graph tf.reset_default_graph() if has_variables: with tf_graph.as_default() as g: saver = tf.train.Saver() # TODO - if Keras backend has_variable is False, we're not making variables constant with tf.Session(graph=tf_graph) as sess: sess.run(tf.global_variables_initializer()) feed_dict = {} for name, shape in input_shapes.items(): tensor_name = tf_graph.get_operation_by_name( name).outputs[0].name feed_dict[tensor_name] = generate_data(shape, data_mode) # run the result fetches = [ tf_graph.get_operation_by_name(name).outputs[0] for name in output_node_names ] result = sess.run(fetches, feed_dict=feed_dict) # save graph definition somewhere tf.train.write_graph(sess.graph, model_dir, graph_def_file, as_text=False) # freeze_graph() has been raising error with tf.keras models since no # later than TensorFlow 1.6, so we're not using freeze_graph() here. # See: https://github.com/tensorflow/models/issues/5387 output_graph_def = tf.graph_util.convert_variables_to_constants( sess, # The session is used to retrieve the weights tf_graph.as_graph_def( ), # The graph_def is used to retrieve the nodes output_node_names # The output node names are used to select the useful nodes ) with tf.gfile.GFile(frozen_model_file, "wb") as f: f.write(output_graph_def.SerializeToString()) K.clear_session() # convert to CoreML mlmodel = coremltools.converters.tensorflow.convert( frozen_model_file, inputs=input_shapes, outputs=output_node_names, use_cpu_only=use_cpu_only) if DEBUG: print('\n mlmodel description: \n') from coremltools.models.neural_network.printer import print_network_spec print_network_spec(mlmodel.get_spec(), style='coding') mlmodel.save(coreml_model_file) print('\n mlmodel saved at %s' % (coreml_model_file)) # Transpose input data as CoreML requires coreml_inputs = { name: tf_transpose(feed_dict[self._get_tf_tensor_name(tf_graph, name)]) for name in input_shapes } # Run predict in CoreML coreml_output = mlmodel.predict(coreml_inputs, useCPUOnly=use_cpu_only) for idx, out_name in enumerate(output_node_names): tf_out = result[idx] if len(tf_out.shape) == 0: tf_out = np.array([tf_out]) tp = tf_out.flatten() coreml_out = coreml_output[out_name] cp = coreml_out.flatten() self.assertTrue(tf_out.shape == coreml_out.shape) for i in range(len(tp)): max_den = max(1.0, tp[i], cp[i]) self.assertAlmostEqual(tp[i] / max_den, cp[i] / max_den, delta=delta) # Cleanup files - models on disk no longer useful if os.path.exists(model_dir): shutil.rmtree(model_dir)
def _test_tf_model_constant(self, graph, input_shapes, output_node_names, data_mode='random_zero_mean', delta=1e-2, use_cpu_only=False, validate_bool_only=False): """ Common entry to testing routine for graphs that have no variables. Parameters ---------- graph: tf.Graph() TensorFlow graph. input_shapes: dict [str : shape] Shapes for each input (placeholder). output_node_names: list of str Output tensor names. data_mode: str Data mode for the placeholder data generation. input_refs: a dictionary of reference input in tensorFlow axis order. Each entry is str:shape. When using auto-generated input vectors, set input_refs to None. delta: float Delta for error checking, default 1e-2. use_cpu_only: bool If true, force use CPU only, default False. validate_bool_only: bool If true, only validate it's zero or non-zero, otherwise, validate float values, default False. """ model_dir = tempfile.mkdtemp() frozen_model_file = os.path.join(model_dir, 'tf_frozen.pb') coreml_model_file = os.path.join(model_dir, 'coreml_model.mlmodel') feed_dict = { self._get_tf_tensor_name(graph, name): generate_data(input_shapes[name], data_mode) for name in input_shapes } with tf.Session(graph=graph) as sess: # initialize sess.run(tf.global_variables_initializer()) # run the result fetches = [ graph.get_operation_by_name(name).outputs[0] for name in output_node_names ] result = sess.run(fetches, feed_dict=feed_dict) output_graph_def = tf.graph_util.convert_variables_to_constants( sess, # The session is used to retrieve the weights tf.get_default_graph().as_graph_def( ), # The graph_def is used to retrieve the nodes output_node_names # The output node names are used to select the useful nodes ) with tf.gfile.GFile(frozen_model_file, 'wb') as f: f.write(output_graph_def.SerializeToString()) # convert to CoreML mlmodel = coremltools.converters.tensorflow.convert( frozen_model_file, inputs=input_shapes, outputs=output_node_names, use_cpu_only=use_cpu_only) if DEBUG: print('\n mlmodel description: \n') from coremltools.models.neural_network.printer import print_network_spec print_network_spec(mlmodel.get_spec(), style='coding') mlmodel.save(coreml_model_file) print('\n mlmodel saved at %s' % coreml_model_file) # Transpose input data as CoreML requires coreml_inputs = { name: tf_transpose(feed_dict[self._get_tf_tensor_name(graph, name)]) for name in input_shapes } # Run predict in CoreML coreml_output = mlmodel.predict(coreml_inputs, useCPUOnly=use_cpu_only) for idx, out_name in enumerate(output_node_names): tf_out = result[idx] if len(tf_out.shape) == 0: tf_out = np.array([tf_out]) tp = tf_out.flatten() coreml_out = coreml_output[out_name] cp = coreml_out.flatten() self.assertTrue(tf_out.shape == coreml_out.shape, msg=(tf_out.shape, 'vs.', coreml_out.shape)) if validate_bool_only: cp = np.logical_and(cp, cp) for i in range(len(tp)): max_den = max(1.0, tp[i], cp[i]) self.assertAlmostEqual(tp[i] / max_den, cp[i] / max_den, delta=delta) # Cleanup files - models on disk no longer useful if os.path.exists(model_dir): shutil.rmtree(model_dir)
def _test_tf_model( self, graph, input_shapes, output_node_names, data_mode='random', input_refs=None, delta=1e-2, use_cpu_only=False, graph_optimizations="freeze", # one of ["freeze", "convert_variables_to_constants", None] quantize_tf_model=False): """ Common entry to testing routine. graph - defined TensorFlow graph. input_shapes - dict str:shape for each input op (placeholder) output_node_names - output_node_names, a list of strings data_mode - auto-generated input vectors, can be 'random', 'zeros', 'ones', 'linear', etc. input_refs - a dictionary of reference input in tensorFlow axis order, each entry is str:shape. When using auto-generated input vectors, set input_refs to None. delta - maximum difference of normalized TensorFlow and CoreML outputs use_cpu_only - If True, instantiate and run CoreML model with CPU only graph_optimizations == "freeze" - Force TensorFlow graph to be frozen before converting. quantize_tf_model - If True, try to quantize TensorFlow model before converting """ # Some file processing model_dir = tempfile.mkdtemp() graph_def_file = os.path.join(model_dir, 'tf_graph.pb') checkpoint_file = os.path.join(model_dir, 'tf_model.ckpt') static_model_file = os.path.join(model_dir, 'tf_static.pb') coreml_model_file = os.path.join(model_dir, 'coreml_model.mlmodel') # add a saver tf.reset_default_graph() if graph_optimizations == "freeze": with graph.as_default() as g: saver = tf.train.Saver() if input_refs is None: feed_dict = { self._get_tf_tensor_name(graph, name): generate_data(input_shapes[name], data_mode) for name in input_shapes } else: feed_dict = { self._get_tf_tensor_name(graph, name): input_refs[name] for name in list(input_refs.keys()) } with tf.Session(graph=graph) as sess: # initialize initializer_op = tf.global_variables_initializer() sess.run(initializer_op) # run the result fetches = [ graph.get_operation_by_name(name).outputs[0] for name in output_node_names ] result = sess.run(fetches, feed_dict=feed_dict) # save graph definition somewhere tf.train.write_graph(sess.graph, model_dir, graph_def_file, as_text=False) # save the weights if freezing is needed if not graph_optimizations: static_model_file = graph_def_file elif graph_optimizations == "freeze": saver.save(sess, checkpoint_file) self._simple_freeze( input_graph=graph_def_file, input_checkpoint=checkpoint_file, output_graph=static_model_file, output_node_names=",".join(output_node_names)) else: output_graph_def = tf.graph_util.convert_variables_to_constants( sess, graph.as_graph_def(), output_node_names) with tf.gfile.GFile(static_model_file, "wb") as f: f.write(output_graph_def.SerializeToString()) # if TF needs to be quantized, quantize the graph if quantize_tf_model: static_model_file = self._quantize_static_tf_model( model_dir, static_model_file, output_node_names) # convert to CoreML mlmodel = coremltools.converters.tensorflow.convert( static_model_file, inputs=input_shapes, outputs=output_node_names, use_cpu_only=use_cpu_only) if DEBUG: print('\n mlmodel description: \n') from coremltools.models.neural_network.printer import print_network_spec print_network_spec(mlmodel.get_spec(), style='coding') mlmodel.save(coreml_model_file) print('\n mlmodel saved at %s' % coreml_model_file) # Transpose input data as CoreML requires coreml_inputs = { name: tf_transpose(feed_dict[self._get_tf_tensor_name(graph, name)]) for name in input_shapes } # Run predict in CoreML coreml_output = mlmodel.predict(coreml_inputs, useCPUOnly=use_cpu_only) for idx, out_name in enumerate(output_node_names): tf_out = result[idx] if len(tf_out.shape) == 0: tf_out = np.array([tf_out]) tp = tf_out.flatten() coreml_out = coreml_output[out_name] cp = coreml_out.flatten() self.assertTrue(tf_out.shape == coreml_out.shape) for i in range(len(tp)): max_den = max(1.0, tp[i], cp[i]) self.assertAlmostEqual(tp[i] / max_den, cp[i] / max_den, delta=delta) # Cleanup files - models on disk no longer useful if os.path.exists(model_dir): shutil.rmtree(model_dir)
def _test_keras_model_tf1(self, model, data_mode, decimal, use_cpu_only, has_variables, verbose): graph_def_file = os.path.join(self.saved_model_dir, 'graph.pb') frozen_model_file = os.path.join(self.saved_model_dir, 'frozen.pb') core_ml_model_file = os.path.join(self.saved_model_dir, 'model.mlmodel') input_shapes = {inp.op.name: inp.shape.as_list() for inp in model.inputs} for name, shape in input_shapes.items(): input_shapes[name] = [dim if dim is not None else 1 for dim in shape] output_node_names = [output.op.name for output in model.outputs] tf_graph = _keras.get_session().graph tf.reset_default_graph() if has_variables: with tf_graph.as_default(): saver = tf.train.Saver() # note: if Keras backend has_variable is False, we're not making variables constant with tf.Session(graph=tf_graph) as sess: sess.run(tf.global_variables_initializer()) feed_dict = {} for name, shape in input_shapes.items(): tensor_name = tf_graph.get_operation_by_name(name).outputs[0].name feed_dict[tensor_name] = generate_data(shape, data_mode) # run the result fetches = [ tf_graph.get_operation_by_name(name).outputs[0] for name in output_node_names ] result = sess.run(fetches, feed_dict=feed_dict) # save graph definition somewhere tf.train.write_graph(sess.graph, self.saved_model_dir, graph_def_file, as_text=False) # freeze_graph() has been raising error with tf.keras models since no # later than TensorFlow 1.6, so we're not using freeze_graph() here. # See: https://github.com/tensorflow/models/issues/5387 output_graph_def = tf.graph_util.convert_variables_to_constants( sess, # The session is used to retrieve the weights tf_graph.as_graph_def(), # The graph_def is used to retrieve the nodes output_node_names # The output node names are used to select the useful nodes ) with tf.gfile.GFile(frozen_model_file, 'wb') as f: f.write(output_graph_def.SerializeToString()) _keras.clear_session() # convert to Core ML model format core_ml_model = coremltools.converters.tensorflow.convert( frozen_model_file, inputs=input_shapes, outputs=output_node_names, use_cpu_only=use_cpu_only) if verbose: print('\nFrozen model saved at {}'.format(frozen_model_file)) print('\nCore ML model description:') from coremltools.models.neural_network.printer import print_network_spec print_network_spec(core_ml_model.get_spec(), style='coding') core_ml_model.save(core_ml_model_file) print('\nCore ML model saved at {}'.format(core_ml_model_file)) # transpose input data as Core ML requires core_ml_inputs = { name: tf_transpose(feed_dict[self._get_tf_tensor_name(tf_graph, name)]) for name in input_shapes } # run prediction in Core ML core_ml_output = core_ml_model.predict(core_ml_inputs, useCPUOnly=use_cpu_only) for idx, out_name in enumerate(output_node_names): tf_out = result[idx] if len(tf_out.shape) == 0: tf_out = np.array([tf_out]) tp = tf_out.flatten() coreml_out = core_ml_output[out_name] cp = coreml_out.flatten() self.assertTrue(tf_out.shape == coreml_out.shape) for i in range(len(tp)): max_den = max(1.0, tp[i], cp[i]) self.assertAlmostEqual(tp[i] / max_den, cp[i] / max_den, delta=10 ** -decimal)