Как экспортировать модель TensorFlow в виде файла .tflite? - PullRequest
0 голосов
/ 29 мая 2018

Справочная информация:

Я написал модель TensorFlow, очень похожую на готовую модель классификации радужной оболочки , предоставленную TensorFlow.Различия относительно незначительны:

  • Я классифицирую футбольные упражнения, а не виды радужной оболочки.
  • У меня есть 10 функций и одна метка, а не 4 функции и одна метка.
  • У меня есть 5 различных упражнений, в отличие от 3 видов радужной оболочки.
  • Мои данные trainData содержат около 3500 строк, а не только 120.
  • Мои данные testData содержат около 330 строк, а не только 30.
  • Я использую классификатор DNN с n_classes = 6, а не 3.

Теперь я хочу экспортировать модель в виде .tflite файла.Но в соответствии с TensorFlow Developer Guide мне нужно сначала экспортировать модель в файл tf.GraphDef, затем заморозить ее, и только тогда я смогу преобразовать ее.Однако учебник , предоставленный TensorFlow для создания файла .pb из пользовательской модели, кажется оптимизированным только для моделей классификации изображений.

Вопрос:

Так как мне преобразовать модель, подобную модели примера классификации радужной оболочки, в файл .tflite?Есть ли более простой и прямой способ сделать это, не экспортируя его в файл .pb, затем заморозив его и так далее?Пример, основанный на коде классификации радужной оболочки или ссылке на более подробное руководство, будет очень полезен!


Другая информация:

  • ОС:macOS 10.13.4 High Sierra
  • Версия TensorFlow: 1.8.0
  • Версия Python: 3.6.4
  • Использование сообщества PyCharm 2018.1.3

Код:

Код классификации радужной оболочки можно клонировать, введя следующую команду:

git clone https://github.com/tensorflow/models

Но если вы этого не сделаетехочу скачать весь пакет, вот он:

Это файл классификатора с именем premade_estimator.py:

    #  Copyright 2016 The TensorFlow Authors. All Rights Reserved.
    #
    #  Licensed under the Apache License, Version 2.0 (the "License");
    #  you may not use this file except in compliance with the License.
    #  You may obtain a copy of the License at
    #
    #  http://www.apache.org/licenses/LICENSE-2.0
    #
    #  Unless required by applicable law or agreed to in writing,                         software
    #  distributed under the License is distributed on an "AS IS" BASIS,
    #  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    #  See the License for the specific language governing permissions and
    #  limitations under the License.
    """An Example of a DNNClassifier for the Iris dataset."""
    from __future__ import absolute_import
    from __future__ import division
    from __future__ import print_function

    import argparse
    import tensorflow as tf

    import iris_data

    parser = argparse.ArgumentParser()
    parser.add_argument('--batch_size', default=100, type=int, help='batch size')
    parser.add_argument('--train_steps', default=1000, type=int,
                help='number of training steps')


    def main(argv):
        args = parser.parse_args(argv[1:])

        # Fetch the data
        (train_x, train_y), (test_x, test_y) = iris_data.load_data()

        # Feature columns describe how to use the input.
        my_feature_columns = []
        for key in train_x.keys():
                    my_feature_columns.append(tf.feature_column.numeric_column(key=key))

        # Build 2 hidden layer DNN with 10, 10 units respectively.
        classifier = tf.estimator.DNNClassifier(
            feature_columns=my_feature_columns,
            # Two hidden layers of 10 nodes each.
            hidden_units=[10, 10],
            # The model must choose between 3 classes.
            n_classes=3)

        # Train the Model.
        classifier.train(
            input_fn=lambda: iris_data.train_input_fn(train_x, train_y,
                                              args.batch_size),
            steps=args.train_steps)

        # Evaluate the model.
        eval_result = classifier.evaluate(
            input_fn=lambda: iris_data.eval_input_fn(test_x, test_y,
                                             args.batch_size))

        print('\nTest set accuracy:         {accuracy:0.3f}\n'.format(**eval_result))

        # Generate predictions from the model
        expected = ['Setosa', 'Versicolor', 'Virginica']
        predict_x = {
            'SepalLength': [5.1, 5.9, 6.9],
            'SepalWidth': [3.3, 3.0, 3.1],
            'PetalLength': [1.7, 4.2, 5.4],
            'PetalWidth': [0.5, 1.5, 2.1],
        }

        predictions = classifier.predict(
            input_fn=lambda: iris_data.eval_input_fn(predict_x,
                                                     labels=None,
                                                     batch_size=args.batch_size))

        template = '\nPrediction is "{}" ({:.1f}%), expected "{}"'

        for pred_dict, expec in zip(predictions, expected):
            class_id = pred_dict['class_ids'][0]
            probability = pred_dict['probabilities'][class_id]

            print(template.format(iris_data.SPECIES[class_id],
                          100 * probability, expec))


    if __name__ == '__main__':
        # tf.logging.set_verbosity(tf.logging.INFO)
        tf.app.run(main)

И это файл данных с именем iris_data.py:

    import pandas as pd
    import tensorflow as tf

    TRAIN_URL = "http://download.tensorflow.org/data/iris_training.csv"
    TEST_URL = "http://download.tensorflow.org/data/iris_test.csv"

    CSV_COLUMN_NAMES = ['SepalLength', 'SepalWidth',
                        'PetalLength', 'PetalWidth', 'Species']
    SPECIES = ['Setosa', 'Versicolor', 'Virginica']


    def maybe_download():
        train_path = tf.keras.utils.get_file(TRAIN_URL.split('/')[-1], TRAIN_URL)
        test_path = tf.keras.utils.get_file(TEST_URL.split('/')[-1], TEST_URL)

        return train_path, test_path


    def load_data(y_name='Species'):
        """Returns the iris dataset as (train_x, train_y), (test_x, test_y)."""
        train_path, test_path = maybe_download()

        train = pd.read_csv(train_path, names=CSV_COLUMN_NAMES, header=0)
        train_x, train_y = train, train.pop(y_name)

        test = pd.read_csv(test_path, names=CSV_COLUMN_NAMES, header=0)
        test_x, test_y = test, test.pop(y_name)

        return (train_x, train_y), (test_x, test_y)


    def train_input_fn(features, labels, batch_size):
        """An input function for training"""
        # Convert the inputs to a Dataset.
        dataset = tf.data.Dataset.from_tensor_slices((dict(features), labels))

        # Shuffle, repeat, and batch the examples.
        dataset = dataset.shuffle(1000).repeat().batch(batch_size)

        # Return the dataset.
        return dataset


    def eval_input_fn(features, labels, batch_size):
        """An input function for evaluation or prediction"""
        features = dict(features)
        if labels is None:
            # No labels, use only features.
            inputs = features
        else:
            inputs = (features, labels)

        # Convert the inputs to a Dataset.
        dataset = tf.data.Dataset.from_tensor_slices(inputs)

        # Batch the examples
        assert batch_size is not None, "batch_size must not be None"
        dataset = dataset.batch(batch_size)

        # Return the dataset.
        return dataset

** ОБНОВЛЕНИЕ **

Хорошо, поэтому я нашел, казалось бы, очень полезный кусок кода на этой странице :

    import tensorflow as tf

    img = tf.placeholder(name="img", dtype=tf.float32, shape=(1, 64, 64, 3))
    val = img + tf.constant([1., 2., 3.]) + tf.constant([1., 4., 4.])
    out = tf.identity(val, name="out")
    with tf.Session() as sess:
      tflite_model = tf.contrib.lite.toco_convert(sess.graph_def, [img], [out])
      open("test.tflite", "wb").write(tflite_model)

Этот маленький парень напрямую преобразует простую модель в модель TensorFlow Lite.Теперь все, что мне нужно сделать, это найти способ адаптировать это к модели классификации радужной оболочки.Есть предложения?

Ответы [ 2 ]

0 голосов
/ 18 апреля 2019

Вот более стандартный способ сделать это вместо использования toco_convert.Спасибо Pannag Sanketi за приведенный выше пример на основе toco, который является основой этого кода.

Обратите внимание, что выходной слой - logits, потому что мы используем классификацию NN.Если бы у нас была регрессия NN, все будет иначе.classifier - это модель NN, которую вы создали.

    def export_tflite(classifier):
        with tf.Session() as sess:
            # First let's load meta graph and restore weights
            latest_checkpoint_path = classifier.latest_checkpoint()
            saver = tf.train.import_meta_graph(latest_checkpoint_path + '.meta')
            saver.restore(sess, latest_checkpoint_path)

            # Get the input and output tensors
            input_tensor = sess.graph.get_tensor_by_name("dnn/input_from_feature_columns/input_layer/concat:0")
            out_tensor = sess.graph.get_tensor_by_name("dnn/logits/BiasAdd:0")

            # here the code differs from the toco example above
            sess.run(tf.global_variables_initializer())
            converter = tf.lite.TFLiteConverter.from_session(sess, [input_tensor], [out_tensor])
            tflite_model = converter.convert()
            open("converted_model.tflite", "wb").write(tflite_model)

0 голосов
/ 01 июня 2018

Есть ли более простой и прямой способ сделать это без необходимости экспортировать его в файл .pb, затем заморозить и т. Д.?

Да, как вы указали в обновленном вопросе, можно заморозить график и использовать toco_convert непосредственно в Python API.Нужно заморозить график и определить входные и выходные формы.В вашем вопросе нет шага стоп-графа, так как нет переменных.Если у вас есть переменные и вы запускаете toco без предварительного преобразования их в константы, то toco будет жаловаться!

Теперь все, что мне нужно сделать, - это найти способ адаптировать это к модели классификации радужной оболочки.Любые предложения?

Это немного сложнее и требует дополнительной работы.По сути, вам нужно загрузить график и выяснить имена тензоров ввода и вывода, а затем заморозить график и вызвать toco_convert.Чтобы найти имена входных и выходных тензоров в этом случае (если вы не определили график), вы должны просмотреть созданный график и определить его на основе форм ввода, имен и т. Д. Вот код, который можно добавить вконец вашей основной функции в premade_estimator.py для генерации графа tflite в этом случае.

print("\n====== classifier model_dir, latest_checkpoint ===========")
print(classifier.model_dir)
print(classifier.latest_checkpoint())
debug = False

with tf.Session() as sess:
    # First let's load meta graph and restore weights
    latest_checkpoint_path = classifier.latest_checkpoint()
    saver = tf.train.import_meta_graph(latest_checkpoint_path + '.meta')
    saver.restore(sess, latest_checkpoint_path)

    # Get the input and output tensors needed for toco.
    # These were determined based on the debugging info printed / saved below.
    input_tensor = sess.graph.get_tensor_by_name("dnn/input_from_feature_columns/input_layer/concat:0")
    input_tensor.set_shape([1, 4])
    out_tensor = sess.graph.get_tensor_by_name("dnn/logits/BiasAdd:0")
    out_tensor.set_shape([1, 3])

    # Pass the output node name we are interested in.
    # Based on the debugging info printed / saved below, pulled out the
    # name of the node for the logits (before the softmax is applied).
    frozen_graph_def = tf.graph_util.convert_variables_to_constants(
        sess, sess.graph_def, output_node_names=["dnn/logits/BiasAdd"])

    if debug is True:
        print("\nORIGINAL GRAPH DEF Ops ===========================================")
        ops = sess.graph.get_operations()
        for op in ops:
            if "BiasAdd" in op.name or "input_layer" in op.name:
                print([op.name, op.values()])
        # save original graphdef to text file
        with open("estimator_graph.pbtxt", "w") as fp:
            fp.write(str(sess.graph_def))

        print("\nFROZEN GRAPH DEF Nodes ===========================================")
        for node in frozen_graph_def.node:
            print(node.name)
        # save frozen graph def to text file
        with open("estimator_frozen_graph.pbtxt", "w") as fp:
            fp.write(str(frozen_graph_def))

tflite_model = tf.contrib.lite.toco_convert(frozen_graph_def, [input_tensor], [out_tensor])
open("estimator_model.tflite", "wb").write(tflite_model)

Примечание: Я предполагаю логиты с последнего слоя (до применения Softmax) в качестве выхода, соответствующего узлу днн / логиты/ BiasAdd .Если вам нужны вероятности, я считаю, что это днн / голова / прогнозы / вероятности .

...