我有一个经过TensorFlow估计器训练的自定义CNN模型(图像分类器),转换为TensorFlowLite模型后,我将在iOS应用中使用它。
我的模型具有多个辍学层以及批处理规范化层。为了避免转换错误并在optimize_for_inference
流程中删除那些辍学层,
我已经在检查点文件旁边分别保存了eval_graph.pbtxt
,以便在freeze_graph
中使用它。
在freeze_graph
中一切正常,并且optimize_for_inference
也不抛出任何错误。但是,将冻结模型文件和优化模型文件(.pb
)都导入张量板进行检查之后,我发现:
似乎optimize_for_inference
删除了输入张量层的形状信息,如果我冻结了使用训练模式保存的图形(默认为graph.pbtxt
)的模型并对其进行优化则不是这种情况。
环境:
代码如下:
model_fn的节选,很正常:
def cnn_model_fn(features, labels, mode, params):
"""Model function for CNN."""
# Input Layer, images aleady reshaped before feed in;
net = tf.placeholder_with_default(
features['Pixels'],
(None, 48, 48, 1),
name='input_tensor'
)
# bn-1
net = tf.layers.batch_normalization(
inputs=net,
training=mode == tf.estimator.ModeKeys.TRAIN
)
# conv2d-1
net = tf.layers.conv2d(
inputs=net,
filters=32,
kernel_size=[3, 3],
padding='same',
activation=tf.nn.relu
)
# conv2ds, dropouts, poolings, bns...
# CONV2Ds -> DENSEs
# 48 pixels pooled three times (kernel_sizes=2, strides=2), and final conv2d has 128 neurons;
net = tf.reshape(net, [-1, 6 * 6 * 128])
# bn-4
net = tf.layers.batch_normalization(
inputs=net,
training=mode == tf.estimator.ModeKeys.TRAIN
)
# dense-1
net = tf.layers.dense(
inputs=net,
units=256,
kernel_regularizer=keras.regularizers.l2(0.001),
activation=tf.nn.relu
)
# denses, logits, nothing special...
# In prediction:
if mode == tf.estimator.ModeKeys.PREDICT:
return tf.estimator.EstimatorSpec(...)
# In evaluation:
if mode == tf.estimator.ModeKeys.EVAL:
# hook for saving graph in eval mode, this graph will be used in freezing & optimizing process;
eval_finish_hook = EvalFinishHook()
eval_finish_hook.model_dir = params['model_dir']
return tf.estimator.EstimatorSpec(
...,
evaluation_hooks=[eval_finish_hook]
)
# In training:
if mode == tf.estimator.ModeKeys.TRAIN:
return tf.estimator.EstimatorSpec(...)
和自定义评估钩子类:
class EvalFinishHook(tf.train.SessionRunHook):
model_dir = '.'
_saver = None
def begin(self):
self._saver = tf.train.Saver()
super().begin()
def end(self, session):
dst_dir = self.model_dir + 'eval_ckpt'
self._saver.save(sess=session, save_path=dst_dir + '/eval.ckpt')
tf.train.write_graph(session.graph.as_graph_def(), dst_dir, 'eval_graph.pbtxt')
super().end(session)
冻结和优化:
# freeze graph
echo "freezing checkpoint ${best_step}..."
freeze_graph \
--input_graph=${input_graph} \
--input_checkpoint=${input_checkpoint} \
--input_binary=false \
--output_graph=${frozen_model} \
--output_node_names=${output_names} \
# optimize for inference
echo "optimizing..."
/path/to/bazel-bin/tensorflow/python/tools/optimize_for_inference \
--input=${frozen_model} \
--output=${optimized_model} \
--frozen_graph=True \
--input_names=${input_names} \
--output_names=${output_names}
toco引发错误:
# convert to tflite
echo "converting..."
toco \
--graph_def_file=${optimized_model} \
--input_format=TENSORFLOW_GRAPHDEF \
--output_format=TFLITE \
--inference_type=FLOAT \
--input_type=FLOAT \
--input_arrays=${input_names} \
--output_arrays=${output_names} \
--input_shapes=1,48,48,1 \
--output_file=${tflite_model}
# error info
Check failed: dim_x == dim_y (128 vs. 4608)Dimensions must match
此错误似乎是合理的,因为形状的等级1和2均未知。
为什么?
答案 0 :(得分:2)
optimize_for_inference随机地从图形中删除掉失层,通常在输入上使用掉失。 因此答案可能是肯定的。
bazel-bin/tensorflow/python/tools/optimize_for_inference \
--input=/tf_files/retrained_graph.pb \
--output=/tf_files/optimized_graph.pb \
--input_names={} \
--output_names=result
让我们尝试使用RandomUniform,FLOOR,TensorFlowShape,TensorFlowSwitch,TensorFlowMerge自定义实现,以禁用错误。
答案 1 :(得分:2)
是的,应该在密集之后:
model.add(Dense(.., ..))
model.add(BatchNormalization())
model.add(Activation(...))
model.add(Dropout(...))
答案 2 :(得分:1)
好吧,似乎交换bn-4
和dense-1
使错误静音。因此,在这种特定情况下,批处理规范化应该在密集之后进行(例如,紧靠conv2d->密集重塑之后)吗?
答案 3 :(得分:0)
在使用冻结图而不是graph.pbtxt时,应使用eval.pbtxt。
请让我们检查tensorflow/models
因此,让我们将第一维的'None'替换为零,其余的替换描述矢量/矩阵的大小。 另一点是要遵守矩阵乘法规则,该规则规定第一个操作数的列数必须与第二个操作数的行数匹配。
如果有帮助,请接受答案。