计算后停止Tensoflow在GPU上运行

时间:2017-07-17 16:38:52

标签: python tensorflow

我在Python中运行REST服务器,具有检索图像的访问点并使用张量流模型来预测该图像上的内容。启动服务器后,我将图像发送到REST端点。加载的模型是我自己训练的Inception模型。它从张量流检查点文件加载以恢复权重。这是构建图形并执行分类的函数:

import os
import tensorflow as tf

from cnn_server.server import file_service as dirs
from slim.datasets import dataset_utils
from slim.nets import nets_factory as network_factory
from slim.preprocessing import preprocessing_factory as preprocessing_factory  

def inference_on_image(bot_id, image_file, network_name='inception_v4', return_labels=1):

        model_path = dirs.get_model_data_dir(bot_id)

        # Get number of classes to predict
        protobuf_dir = dirs.get_protobuf_dir(bot_id)
        number_of_classes = dataset_utils.get_number_of_classes_by_labels(protobuf_dir)

        # Get the preprocessing and network construction functions
        preprocessing_fn = preprocessing_factory.get_preprocessing(network_name, is_training=False)
        network_fn = network_factory.get_network_fn(network_name, number_of_classes)

        # Process the temporary image file into a Tensor of shape [widht, height, channels]
        image_tensor = tf.gfile.FastGFile(image_file, 'rb').read()
        image_tensor = tf.image.decode_image(image_tensor, channels=0)

        # Perform preprocessing and reshape into [network.default_width, network.default_height, channels]
        network_default_size = network_fn.default_image_size
        image_tensor = preprocessing_fn(image_tensor, network_default_size, network_default_size)

        # Create an input batch of size one from the preprocessed image
        input_batch = tf.reshape(image_tensor, [1, 299, 299, 3])

        # Create the network up to the Predictions Endpoint
        logits, endpoints = network_fn(input_batch)

        restorer = tf.train.Saver()

        with tf.Session() as sess:
            tf.global_variables_initializer().run()

            # Restore the variables of the network from the last checkpoint and run the graph
            restorer.restore(sess, tf.train.latest_checkpoint(model_path))
            sess.run(endpoints)

            # Get the numpy array of predictions out of the
            predictions = endpoints['Predictions'].eval()[0]
            sess.close()

        return map_predictions_to_labels(protobuf_dir, predictions, return_labels)

为了构建Inception V4模型的图形,我使用了tf.model.slim,这是一系列最先进的CCN的张量流实现。初始模型在此处构建:https://github.com/tensorflow/models/blob/master/slim/nets/inception_v4.py并通过工厂方法提供:https://github.com/tensorflow/models/blob/master/slim/nets/nets_factory.py

对于第一张图片,everythig按预期工作:

2017-07-17 18:00:43.831365: I tensorflow/core/common_runtime/gpu/gpu_device.cc:908] DMA: 0 
2017-07-17 18:00:43.831371: I tensorflow/core/common_runtime/gpu/gpu_device.cc:918] 0:   Y 
2017-07-17 18:00:43.831384: I tensorflow/core/common_runtime/gpu/gpu_device.cc:977] Creating TensorFlow device (/gpu:0) -> (device: 0, name: GeForce GTX 1080, pci bus id: 0000:01:00.0)
192.168.0.192 - - [17/Jul/2017 18:00:46] "POST /classify/4 HTTP/1.1" 200 -

第二个图像会产生以下错误:

ValueError: Variable InceptionV4/Conv2d_1a_3x3/weights already exists, disallowed. Did you mean to set reuse=True in VarScope? Originally defined at:

我对此的理解是,图表最初是创建的,然后继续存在于某个地方。发送第二个图像会导致再次调用该函数,尝试重新创建现有图形,然后再出现错误。现在我尝试了一些东西:

整体停止Tensorflow: 我试图在整体上停止tensorflow并且每次在GPU上重新创建设备。这将是最好的解决方案,因为这样在服务器运行时GPU不会被Tensorflow占用。我尝试用sess.close()做到这一点,但是没有用。在处理完第一张图片后,nvidia-smi仍会在GPU上显示该过程。然后我尝试以某种方式访问​​设备,但我只能通过device_lib.list_local_devices()获得可用设备列表。但是,这并没有导致操作GPU上的tensorflow进程的任何选项。停止服务器,即启动张量流会话的初始python脚本也会消除GPU上的张量流。每次分类后重新启动服务器都不是一个优雅的解决方案。

重置或删除图表 我试图以几种方式重置图表。一种方法是从我正在运行的张量中检索图形,迭代所有集合并清除它们:

graph = endpoints['Predictions'].graph
for key in graph.get_all_collection_keys():
    graph.clear_collection(key)

调试显示之后图表集合为空,但错误保持不变。另一种方法是将图表从端点设置为默认图形with graph.as_default:,因为图形已经创建之前我没有希望这会在计算后删除图形。它没有。

将变量范围设置为reuse=true 变量范围有一个选项重用,您可以在inception_v4.py

中设置
def inception_v4(inputs, num_classes=1001, is_training=True,
                 dropout_keep_prob=0.8,
                 reuse=None,
                 scope='InceptionV4',
                 create_aux_logits=True):

将其设置为true会导致最初创建图表时出错,并说变量不存在。

加载模型一次,然后重新加载 我想到的另一种方法是创建模型一次然后重复使用它,即避免第二次调用网络工厂。现在这是有问题的,因为服务器拥有多个模型,每个模型在不同数量的类上工作。这意味着,我必须为每个模型创建图形,让它们保持活着并以某种方式维护它们。虽然这是可能的,但它会导致很多开销并且有点多余,因为模型总是相同的,只是权重和最终层不同。权重已存储在检查点文件中,tf.model.slim中的实现允许轻松创建具有不同类别的输出的图形。

我在这里没有想法。最理想的解决方案当然是完全终止GPU上的张量流,并在每次调用函数时从头开始重新创建设备。

希望有人可以在这里提供帮助。

提前致谢。

2 个答案:

答案 0 :(得分:2)

让我们一个接一个地解决你的问题。

首先,关于已存在的变量的错误来自您重用现有图并在每个请求上重新运行模型创建代码。通过在with tf.Graph().as_default():函数中添加inference_on_image上下文管理器来创建每个请求的图表,或者(强烈推荐)重用图表,方法是将该函数的一部分分开session.run模型建立和重量加载的网络。

对于第二个问题,没有办法让tensorflow重置其GPU状态而不会终止整个过程。

对于第三个问题,清除图表集合不会有太大作用。您可以为每个请求使用一个新图表,但默认情况下仍然会共享变量的状态,因为它们将驻留在GPU上。你可以使用session.reset来清除那个状态,但是这不会让你的ram回来。

要在共享权重的同时重用具有不同类数的模型,听起来你需要有一个构造所有类的函数。我认为最好的方法是改变slim方法的实现以返回到最后一层,然后让你自己的代码添加完全连接的层,并在其上添加正确数量的类。

当然,除非您将所有模型训练在一起,否则您可能仍然需要网络其余部分的不同参数值。

答案 1 :(得分:0)

我在这里找到了问题的解决方案:https://stackoverflow.com/a/44842044/7208993

这个想法是在一个进程中执行该函数,该函数在执行后终止。可以通过与Manager()对象共享变量来获得结果。虽然这可能不是最优雅的解决方案,但tensorflow似乎并没有提供更好的方法。由于在整个服务器运行时,Tensorflow没有占用GPU,这已经足够了。代码现在看起来像这样:

    def inference_on_image(bot_id, image_file, network_name='inception_v4', return_labels=1):
        manager = Manager()
        prediction_dict = manager.dict()
        process = multiprocessing.Process(target=infere, args=(bot_id, image_file, network_name, return_labels, prediction_dict))
        process.start()
        process.join()
        return prediction_dict['predictions']


    def infere(bot_id, image_file, network_name='inception_v4', return_labels=1, prediction_dict=[]):
        # Get the model path
        model_path = dirs.get_model_data_dir(bot_id)

        # Get number of classes to predict
        protobuf_dir = dirs.get_protobuf_dir(bot_id)
        number_of_classes = dataset_utils.get_number_of_classes_by_labels(protobuf_dir)

        # Get the preprocessing and network construction functions
        preprocessing_fn = preprocessing_factory.get_preprocessing(network_name, is_training=False)
        network_fn = network_factory.get_network_fn(network_name, number_of_classes)

        # Process the temporary image file into a Tensor of shape [widht, height, channels]
        image_tensor = tf.gfile.FastGFile(image_file, 'rb').read()
        image_tensor = tf.image.decode_image(image_tensor, channels=0)

        # Perform preprocessing and reshape into [network.default_width, network.default_height, channels]
        network_default_size = network_fn.default_image_size
        image_tensor = preprocessing_fn(image_tensor, network_default_size, network_default_size)

        # Create an input batch of size one from the preprocessed image
        input_batch = tf.reshape(image_tensor, [1, 299, 299, 3])

        # Create the network up to the Predictions Endpoint
        logits, endpoints = network_fn(input_batch)

        restorer = tf.train.Saver()

        with tf.Session() as sess:
            tf.global_variables_initializer().run()

            # Restore the variables of the network from the last checkpoint and run the graph
            restorer.restore(sess, tf.train.latest_checkpoint(model_path))
            sess.run(endpoints)

            # Get the numpy array of predictions out of the
            predictions = endpoints['Predictions'].eval()[0]
            sess.close()
            graph = endpoints['Predictions'].graph

            prediction_dict['predictions'] = map_predictions_to_labels(protobuf_dir, predictions, return_labels)