2017-07-17 10 views
0

私はPythonでRESTサーバーを実行しています。アクセスポイントで画像を取得し、テンソルフローモデルを使用して画像上のものを予測します。サーバーを起動した後、イメージをRESTエンドポイントに送信しています。ロードされたモデルは、自分で訓練したインセプションモデルです。これはテンソルフローのチェックポイントファイルからロードされ、重みを復元します。計算後にGPUでTensoflowが動作しないようにします

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) 

私は、最先端のCCNののtensorflow実装のコレクションをtf.model.slimを使用インセプションV4モデルのグラフを作成するには:ここでグラフを構築し、分類を実行機能があります。当初のモデルは、ここで構築されています: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最初の画像の場合:

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

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 - 

第二の画像は、次のエラーを作成します

これを私が理解するのは、グラフが最初に作成され、次にどこかに存在するということです。 2番目のイメージを送信すると、関数を再度呼び出すことになり、既存のグラフを再作成しようとするとエラーが発生します。今、私はいくつかのことを試してみました:

全体のTensorflowを停止

私はtensorflow全体的に停止し、GPU上でデバイスを毎回再作成してみました。これは最善の解決策です。この方法では、サーバーが動作しているときにGPUがTensorflowによって占有されないためです。私はsess.close()でそれをやろうとしましたが、うまくいきませんでした。 nvidia-smiは、最初のイメージを処理した後もGPUでプロセスを表示します。何とかそのデバイスにアクセスしようとしましたが、利用できるデバイスの一覧はdevice_lib.list_local_devices()で取得できます。しかし、これによってGPU上のテンソルフロープロセスを操作するオプションはありませんでした。サーバを停止すると、テンソルフローセッションを開始した初期のPythonスクリプトも、GPUのテンソルフローを強制終了します。各分類後にサーバーを再起動するのは、洗練された解決策ではありません。

グラフのリセットまたは削除 グラフをいくつかの方法でリセットしようとしました。しかし、エラーが同じまま、

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

デバッグがグラフコレクションはその後空であることを示しています。一つの方法は、私が実行していますテンソルからグラフを取得するすべてのコレクションを反復処理し、それらをクリアすることです。もう1つの方法は、デフォルトのグラフとして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に設定すると、最初にグラフを作成する変数が存在しないというエラーが発生します。

それは私が考えるもう一つの方法は、 一度モデルを作成し、次にそれを再利用し resuing、一度モデルをロードする、すなわち、ネットワーク工場をもう一度呼び出すことは避けてください。これは、サーバーが複数のモデルを保持し、それぞれ異なる数のクラスで動作するため、問題があります。これは、これらのモデルのそれぞれについてグラフを作成し、それらをすべて生きて維持し、何らかの形で維持しなければならないことを意味します。これは可能ですが、モデルが常に同じで、重さと最終レイヤーが異なるだけなので、オーバーヘッドが大きくなり、いくぶん冗長です。重みは既にチェックポイントファイルに格納されており、実装ではtf.model.slimを使用すると、出力のクラス数が異なるグラフを簡単に作成できます。

私はここでアイデアがありません。最も望ましい解決策は、GPU上でテンソルフローを完全に終了し、関数が呼び出されるたびにゼロからデバイスを再作成することです。

誰でも助けてくれることを願っています。

ありがとうございます。

答えて

0

私はここでの問題への解決策を見つけた:https://stackoverflow.com/a/44842044/7208993

をアイデアは実行後に終了過程で機能を実行することです。結果は、Manager()オブジェクトで変数を共有することによって実現できます。これはおそらく最も洗練された解決策ではありませんが、テンソルフローは今より良い方法を提供していないようです。 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

問題を1つずつ解決しましょう。

最初に、既存の変数についてのエラーは、既存のグラフを再利用し、すべてのリクエストでモデル作成コードを再実行したことによるものです。 コンテキストマネージャーをinference_on_image関数の中に追加するか、またはネットワーク上のsession.runを実行する関数の部分をモデルビルドとウェイトローディングから分離して、グラフを再利用する(強くお勧めします)。

第2の問題として、テンソルフローをGPUの状態をリセットしてプロセス全体を終了させることはできません。

第3の問題については、グラフコレクションをクリアすることはあまりありません。リクエストごとに新しいグラフを使用することはできますが、デフォルトでは変数の状態はGPUに保存されるため、共有されます。あなたはその状態をクリアするためにsession.resetを使うことができますが、これはあなたのラムを元に戻すことはありません。

重み付けを共有している間に異なる数のクラスでモデルを再利用するには、それらをすべて構築する関数が必要なように思えます。私はそれを行う最善の方法は、最後の層に戻るためのスリムメソッドの実装を変更することです、そして、あなた自身のコードを持って、その上にクラスの適切な数で完全に接続された層を追加します。

もちろん、すべてのモデルを一緒に訓練しない限り、残りのネットワークのパラメータ値を変えたいと思うかもしれません。

+0

返信用のタンク。グラフを再利用するということは、いくつかの非常に大きな開始v4モデルをメモリ内に保持することを意味します。それは私にとっては良い考えのようには思えません。特に、グラフを削除する手段を提供しているようです。また、テンソルフローが依然としてGPUを占めるという問題が残っている。標準的な設定では、その間に他のプロセスがGPU上で何もすることはできませんが、これも望ましくありません。私は推論の後に終了する専用のスレッドで関数を開始することを選択しました(私の答えは以下を参照)。それは私のためにはうまくいく。 – molig

関連する問題