使用toco进行tflite转换时出现“尺寸必须匹配”错误

时间:2019-05-13 10:12:22

标签: python tensorflow tensorboard tensorflow-estimator tensorflow-lite

我有一个经过TensorFlow估计器训练的自定义CNN模型(图像分类器),转换为TensorFlowLite模型后,我将在iOS应用中使用它。

我的模型具有多个辍学层以及批处理规范化层。为了避免转换错误并在optimize_for_inference流程中删除那些辍学层, 我已经在检查点文件旁边分别保存了eval_graph.pbtxt,以便在freeze_graph中使用它。

freeze_graph中一切正常,并且optimize_for_inference也不抛出任何错误。但是,将冻结模型文件和优化模型文件(.pb)都导入张量板进行检查之后,我发现:

优化前的冻结模型 freezed model before optimization

优化后的模型 model after optimization

似乎optimize_for_inference删除了输入张量层的形状信息,如果我冻结了使用训练模式保存的图形(默认为graph.pbtxt)的模型并对其进行优化则不是这种情况。

环境:

  • 用于训练的Tensorflow 1.8.0;
  • Tensorflow 1.13.1进行转换;

代码如下:

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均未知。

为什么?

4 个答案:

答案 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自定义实现,以禁用错误。

参考:Dropout Regularization

答案 1 :(得分:2)

是的,应该在密集之后:

model.add(Dense(.., ..))
model.add(BatchNormalization())
model.add(Activation(...))
model.add(Dropout(...))

答案 2 :(得分:1)

好吧,似乎交换bn-4dense-1使错误静音。因此,在这种特定情况下,批处理规范化应该在密集之后进行(例如,紧靠conv2d->密集重塑之后)吗?

答案 3 :(得分:0)

在使用冻结图而不是graph.pbtxt时,应使用eval.pbtxt。

请让我们检查tensorflow/models

因此,让我们将第一维的'None'替换为零,其余的替换描述矢量/矩阵的大小。 另一点是要遵守矩阵乘法规则,该规则规定第一个操作数的列数必须与第二个操作数的行数匹配。

如果有帮助,请接受答案。