Skip to main content
 首页 » 编程设计

python之如何将 TensorFlow 模型导出为 .tflite 文件

2024年12月31日31zfyouxi

背景信息:

我编写了一个与 premade iris classification model 非常相似的 TensorFlow 模型由 TensorFlow 提供。差异相对较小:

  • 我正在对足球运动进行分类,而不是鸢尾属物种。
  • 我有 10 个特征和一个标签,而不是 4 个特征和一个标签。
  • 我有 5 种不同的练习,而不是 3 种鸢尾花。
  • 我的 trainData 包含大约 3500 行,而不仅仅是 120 行。
  • 我的测试数据包含大约 330 行,而不仅仅是 30 行。
  • 我正在使用 n_classes=6 而不是 3 的 DNN 分类器。

我现在想将模型导出为 .tflite 文件。但是根据TensorFlow Developer Guide ,我需要先将模型导出到 tf.GraphDef 文件,然后卡住它,然后才能转换它。然而,tutorial 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 

** 更新 **

好的,所以我找到了一段看似非常有用的代码 on this page :

    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 模型。现在我所要做的就是找到一种方法使其适应虹膜分类模型。有什么建议吗?

请您参考如下方法:

Is there an easier, more direct way to do it, without having to export it to a .pb file, then freeze it and so on?

是的,正如您在更新的问题中指出的那样,可以 freeze the graph并使用 toco_convert直接在python api中。它需要卡住图形并确定输入和输出形状。在您的问题中,没有卡住图步骤,因为没有变量。如果你有变量并运行 toco 而没有先将它们转换为常量,toco 会报错!

Now all I have to do is find a way to adapt this to the iris classification model. Any suggestions?

这个有点棘手,需要做更多的工作。基本上,您需要加载图形并找出输入和输出张量名称,然后卡住图形并调用 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 之前)的 logits 作为输出,对应于节点 dnn/logits/BiasAdd。如果您想要概率,我相信它是 dnn/head/predictions/probabilities。