【问题标题】:Tensorflow: Layer size dependent on batch size?Tensorflow:层大小取决于批量大小?
【发布时间】:2018-02-21 22:25:00
【问题描述】:

我目前正在尝试熟悉 Tensorflow 库,但有一个相当基本的问题困扰着我。

在为 MNIST 分类构建卷积神经网络时,我尝试使用自己的 model_fn。其中通常会出现以下行来重塑输入特征。

x = tf.reshape(x, shape=[-1, 28, 28, 1]),其中 -1 表示输入批量大小。

由于我使用此节点作为卷积层的输入,

x = tf.reshape(x, shape=[-1, 28, 28, 1]) 
conv1 = tf.layers.conv2d(x, 32, 5, activation=tf.nn.relu)

这是否意味着我所有网络层的大小都取决于批量大小?

我尝试在单个测试输入上冻结并运行图表,这仅在我提供 n=batch_size 测试图像时才有效。

你能告诉我如何让我的网络在预测时运行在任何输入批量大小上吗? 另外我猜想在网络定义中使用 tf.reshape 节点(查看 cnn_layout 中的第一个节点)并不是服务的最佳输入。

我将附加我的网络层和 model_fn

def cnn_layout(features,reuse,is_training):
 with tf.variable_scope('cnn',reuse=reuse):
    # resize input to [batchsize,height,width,channel]
    x = tf.reshape(features['x'], shape=[-1,30,30,1], name='input_placeholder')
    # conv1, 32 filter, 5 kernel
    conv1 = tf.layers.conv2d(x, 32, 5, activation=tf.nn.relu, name='conv1')
    # pool1, 2 stride, 2 kernel
    pool1 = tf.layers.max_pooling2d(conv1, 2, 2, name='pool1')
    # conv2, 64 filter, 3 kernel
    conv2 = tf.layers.conv2d(pool1, 64, 3, activation=tf.nn.relu, name='conv2')
    # pool2, 2 stride, 2 kernel
    pool2 = tf.layers.max_pooling2d(conv2, 2, 2, name='pool2')
    # flatten pool2
    flatten = tf.contrib.layers.flatten(pool2)
    # fc1 with 1024 neurons
    fc1 = tf.layers.dense(flatten, 1024, name='fc1')
    # 75% dropout
    drop = tf.layers.dropout(fc1, rate=0.75, training=is_training, name='dropout')
    # output logits
    output = tf.layers.dense(drop, 1, name='output_logits')
    return output


def model_fn(features, labels, mode):
    # setup two networks one for training one for prediction while sharing weights
    logits_train = cnn_layout(features=features,reuse=False,is_training=True)
    logits_test = cnn_layout(features=features,reuse=True,is_training=False)

    # predictions
    predictions = tf.round(tf.sigmoid(logits_test),name='predictions')
    if mode == tf.estimator.ModeKeys.PREDICT:
        return tf.estimator.EstimatorSpec(mode, predictions=predictions)

    # define loss and optimizer
    loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=logits_train,labels=labels),name='loss')
    optimizer = tf.train.AdamOptimizer(learning_rate=LEARNING_RATE, name='optimizer')
    train = optimizer.minimize(loss, global_step=tf.train.get_global_step(),name='train')

    # accuracy for evaluation
    accuracy = tf.metrics.accuracy(labels=labels,predictions=predictions,name='accuracy')

    # summarys for tensorboard
    tf.summary.scalar('loss',loss)

    # return training and evalution spec
    return tf.estimator.EstimatorSpec(
        mode=mode,
        predictions=predictions,
        loss=loss,
        train_op=train,
        eval_metric_ops={'accuracy':accuracy}
    )

谢谢!

【问题讨论】:

    标签: python tensorflow tensorflow-serving


    【解决方案1】:

    在典型情况下,features['x'] 的等级已经是 4,外部维度是实际批量大小,因此无需调整大小。

    让我试着解释一下。

    你还没有显示你的serving_input_receiver_fn,有几种方法可以做到这一点,尽管最终它们的原理是相似的。如果您使用的是 TensorFlow Serving,那么您可能会使用 build_parsing_serving_input_receiver_fn。看看源代码就知道了:

    def build_parsing_serving_input_receiver_fn(feature_spec,
                                                default_batch_size=None):    
      serialized_tf_example = array_ops.placeholder(
          dtype=dtypes.string,
          shape=[default_batch_size],                                      
          name='input_example_tensor')
      receiver_tensors = {'examples': serialized_tf_example}
      features = parsing_ops.parse_example(serialized_tf_example, feature_spec)
      return ServingInputReceiver(features, receiver_tensors)
    

    因此,在您的客户端中,您将准备一个包含一个或多个Examples 的请求(假设长度为N)。服务器将序列化的示例视为字符串列表,这些字符串会“输入”到input_example_tensor 占位符中。形状(None)动态填充为列表的大小(N)。

    然后parse_example 操作解析占位符中的每个项目,并为每个外部维度为N 的特征弹出一个张量。在您的情况下,您将拥有 shape=[N, 30, 30, 1] 的 x

    (注意其他服务系统,比如CloudML Engine,不对Example对象进行操作,但是原理是一样的)。

    【讨论】:

    • 我没有使用serving_input_receiver,但我使用graph_util.convert_variables_to_constants() 冻结了图表,然后调用with tf.gfile.GFile(output_path, "wb") as f: f.write(output_graph_def.SerializeToString())。但我想这也会将小批量大小冻结到图中。所以只是要清楚。我需要一种服务服务器后端而不是标准的 tensorflow 库来执行训练有素的模型吗?由于我的模型非常小并且执行速度应该非常快,因此我正在寻找一个相当精简的解决方案。
    • 我不确定冻结图形是否会将批量大小冻结成图形;手头,我会说它不应该。请注意,您可以在没有服务器后端的情况下相对简单地执行经过训练的模型。我建议将您的模型导出为 SavedModel(您仍然可以冻结您的图表)。这为您提供了更好的可移植性和可以说是更好的推理库(如果以后有必要,还可以选择轻松升级到更高级的服务解决方案)。请参阅 stackoverflow.com/a/46139198/1399222 了解如何使用 SavedModel 进行预测。
    • 如果您不想使用 SavedModel,基本思路是使用tensorflow.python.training.saver.import_meta_graph,然后您可以使用session.Run 来提供输入并获取输出。诀窍是获取输入和输出张量的正确名称。
    • 提供的线程确实帮助我解决了我的问题。现在预测也快了很多。建立生产级服务不是我的目标,而是找到一种在本地运行我的网络的简单方法。不过还是谢谢你。
    • 很高兴听到这个消息!
    【解决方案2】:

    我只想简单地提供我找到的解决方案。因为我不想构建一个可扩展的生产级模型,而是一个简单的 Python 模型运行器来在本地执行我的 CNN。

    要导出我使用的模型,

    input_size = 900
    
    def serving_input_receiver_fn():
        inputs = {"x": tf.placeholder(shape=[None, input_size], dtype=tf.float32)}
        return tf.estimator.export.ServingInputReceiver(inputs, inputs)
    
    model.export_savedmodel(
        export_dir_base=model_dir,
        serving_input_receiver_fn=serving_input_receiver_fn)
    

    为了加载和运行它(无需再次定义模型),我使用了 tensorflow 预测器类。

    from tensorflow.contrib import predictor
    
    class TFRunner:
        """ runs a frozen cnn graph """
        def __init__(self,model_dir):
            self.predictor = predictor.from_saved_model(model_dir)
    
        def run(self, input_list):
            """ runs the input list through the graph, returns output """
            if len(input_list) > 1:
                inputs = np.vstack(input_list)
                predictions = self.predictor({"x": inputs})
            elif len(input_list) == 1:
                predictions = self.predictor({"x": input_list[0]})
            else:
                predictions = []
            return predictions
    

    【讨论】:

      猜你喜欢
      • 2018-01-27
      • 2016-09-22
      • 2019-06-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-19
      • 2017-08-28
      相关资源
      最近更新 更多