Exemple #1
0
    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)