【问题标题】:Stop Tensoflow from running on the GPU after a computation计算后停止 TensorFlow 在 GPU 上运行
【发布时间】:2017-12-22 07:09:42
【问题描述】:

我在 Python 中运行一个 REST 服务器,带有一个访问点来检索图像并使用 tensorflow 模型来预测该图像上的内容。启动服务器后,我将图像发送到 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 的 tensorflow 实现的集合。初始模型在这里构建:https://github.com/tensorflow/models/blob/master/slim/nets/inception_v4.py,并通过工厂方法提供:https://github.com/tensorflow/models/blob/master/slim/nets/nets_factory.py

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

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 进程的任何选项。停止服务器,即启动 tensorflow 会话的初始 python 脚本也会杀死 GPU 上的 tensorflow。每次分类后重新启动服务器并不是一个优雅的解决方案。

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

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

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

将变量范围设置为reuse=true 变量作用域有一个选项reuse,可以在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 上的 tensorflow,并在每次调用函数时从头开始重新创建设备。

希望任何人都可以在这里提供帮助。

提前致谢。

【问题讨论】:

    标签: python tensorflow


    【解决方案1】:

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

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

        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)
    

    【讨论】:

      【解决方案2】:

      让我们一一解决你的问题。

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

      对于第二个问题,没有办法让 tensorflow 在不终止整个进程的情况下重置其 GPU 状态。

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

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

      当然,您可能仍然希望网络的其余部分使用不同的参数值,除非您一起训练所有模型。

      【讨论】:

      • 感谢回复。重用图意味着在内存中保留几个相当大的 inception v4 模型。这对我来说似乎不是一个好主意,特别是因为 tf 似乎没有提供任何删除图表的方法。问题仍然存在,张量流仍然占据 GPU。使用标准配置,同时其他进程无法在 GPU 上执行任何操作,这也是不可取的。我选择在专用线程中启动该函数,该线程在推理后终止(请参阅下面的答案)。这对我最有效。
      猜你喜欢
      • 2020-03-02
      • 1970-01-01
      • 2017-12-03
      • 2018-11-01
      • 1970-01-01
      • 2018-04-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多