如何将 TensorFlow 模型导出为 .tflite 文件?
How do I export a TensorFlow model as a .tflite file?
背景资料:
我编写了一个与 TensorFlow 提供的 premade iris classification model 非常相似的 TensorFlow 模型。差异相对较小:
- 我分类的是足球练习,不是鸢尾花种类。
- 我有 10 个特征和一个标签,而不是 4 个特征和一个标签。
- 我有 5 种不同的练习,而不是 3 种鸢尾花。
- 我的 trainData 包含大约 3500 行,而不仅仅是 120 行。
- 我的测试数据包含大约 330 行,而不仅仅是 30 行。
- 我正在使用 n_classes=6 而不是 3 的 DNN 分类器。
我现在想将模型导出为 .tflite
文件。但是根据 TensorFlow 提供的 TensorFlow Developer Guide, I need to first export the model to a tf.GraphDef
file, then freeze it and only then will I be able to convert it. However, the tutorial 从自定义模型创建 .pb
文件似乎只针对图像分类模型进行了优化。
问题:
那么如何将像鸢尾花分类示例模型这样的模型转换成 .tflite
文件呢?有没有更简单、更直接的方法,无需将其导出到 .pb
文件,然后将其冻结等等?基于虹膜分类代码的示例或 link 更明确的教程将非常有用!
其他信息:
- OS: 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?
是的,正如您在更新后的问题中指出的那样,可以直接在 python api 中 freeze the graph and use toco_convert。它需要冻结图形并确定输入和输出形状。在您的问题中,没有冻结图步骤,因为没有变量。如果你有变量和 运行 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.
这里有一个比 toco_convert 更标准的方法。感谢 Pannag Sanketi 上面基于 toco 的示例,这是此代码的基础。
请注意,输出层是 logits
,因为我们使用的是分类神经网络。如果我们有回归神经网络,情况就会不同。
classifier
是您构建的神经网络模型。
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)
背景资料:
我编写了一个与 TensorFlow 提供的 premade iris classification model 非常相似的 TensorFlow 模型。差异相对较小:
- 我分类的是足球练习,不是鸢尾花种类。
- 我有 10 个特征和一个标签,而不是 4 个特征和一个标签。
- 我有 5 种不同的练习,而不是 3 种鸢尾花。
- 我的 trainData 包含大约 3500 行,而不仅仅是 120 行。
- 我的测试数据包含大约 330 行,而不仅仅是 30 行。
- 我正在使用 n_classes=6 而不是 3 的 DNN 分类器。
我现在想将模型导出为 .tflite
文件。但是根据 TensorFlow 提供的 TensorFlow Developer Guide, I need to first export the model to a tf.GraphDef
file, then freeze it and only then will I be able to convert it. However, the tutorial 从自定义模型创建 .pb
文件似乎只针对图像分类模型进行了优化。
问题:
那么如何将像鸢尾花分类示例模型这样的模型转换成 .tflite
文件呢?有没有更简单、更直接的方法,无需将其导出到 .pb
文件,然后将其冻结等等?基于虹膜分类代码的示例或 link 更明确的教程将非常有用!
其他信息:
- OS: 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?
是的,正如您在更新后的问题中指出的那样,可以直接在 python api 中 freeze the graph and use toco_convert。它需要冻结图形并确定输入和输出形状。在您的问题中,没有冻结图步骤,因为没有变量。如果你有变量和 运行 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.
这里有一个比 toco_convert 更标准的方法。感谢 Pannag Sanketi 上面基于 toco 的示例,这是此代码的基础。
请注意,输出层是 logits
,因为我们使用的是分类神经网络。如果我们有回归神经网络,情况就会不同。
classifier
是您构建的神经网络模型。
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)