web-dev-qa-db-ja.com

TensorFlow推論

私はしばらくの間これについて掘り下げてきました。たくさんの記事を見つけました。しかし、単なるテンソルフロー推論を単純な推論として実際に示すものはありません。常に「サービングエンジンを使用する」か、事前にコード化/定義されたグラフを使用します。

ここに問題があります:更新されたモデルを時々チェックするデバイスがあります。次に、そのモデルをロードし、モデル全体で入力予測を実行する必要があります。

ケラでは、これは簡単でした。モデルを構築します。モデルをトレーニングし、model.predict()を呼び出します。 scikit-learn同じことで。

新しいモデルを取得してロードできます。すべての重量を印刷できます。しかし、どのように私はそれに対して推論を実行しますか?

モデルをロードしてウェイトを印刷するコード:

_    with tf.Session() as sess:
        new_saver = tf.train.import_meta_graph(MODEL_PATH + '.meta', clear_devices=True)
        new_saver.restore(sess, MODEL_PATH)
        for var in tf.trainable_variables():
            print(sess.run(var))
_

私はすべてのコレクションを印刷しました。['queue_runners'、 'variables'、 'losses'、 'summaries'、 'train_op'、 'cond_context'、 'trainable_variables']

sess.run(train_op);を使用してみましたしかし、それはちょうど完全なトレーニングセッションを開始し始めました。これは私がやりたいことではありません。 TFレコードではない、提供する別の入力セットに対して推論を実行したいだけです。

もう少し詳しく:

デバイスはC++またはPythonを使用できます。 .exeを生成できる限り。システムにフィードを送りたい場合は、フィード辞書を設定できます。 TFRecordsでトレーニングしました。しかし、本番環境ではTFRecordsを使用しません。そのリアルタイム/ニアリアルタイムシステム。

ご意見ありがとうございます。このレポにサンプルコードを投稿しています: https://github.com/drcrook1/CIFAR10/TensorFlow これは、すべてのトレーニングとサンプル推論を行います。

ヒントは大歓迎です!

------------ EDITS -----------------次のようにモデルを再構築しました:

_def inference(images):
    '''
    Portion of the compute graph that takes an input and converts it into a Y output
    '''
    with tf.variable_scope('Conv1') as scope:
        C_1_1 = ld.cnn_layer(images, (5, 5, 3, 32), (1, 1, 1, 1), scope, name_postfix='1')
        C_1_2 = ld.cnn_layer(C_1_1, (5, 5, 32, 32), (1, 1, 1, 1), scope, name_postfix='2')
        P_1 = ld.pool_layer(C_1_2, (1, 2, 2, 1), (1, 2, 2, 1), scope)
    with tf.variable_scope('Dense1') as scope:
        P_1 = tf.reshape(C_1_2, (CONSTANTS.BATCH_SIZE, -1))
        dim = P_1.get_shape()[1].value
        D_1 = ld.mlp_layer(P_1, dim, NUM_DENSE_NEURONS, scope, act_func=tf.nn.relu)
    with tf.variable_scope('Dense2') as scope:
        D_2 = ld.mlp_layer(D_1, NUM_DENSE_NEURONS, CONSTANTS.NUM_CLASSES, scope)
    H = tf.nn.softmax(D_2, name='prediction')
    return H
_

後で取得できるように、TF操作に_'prediction'_という名前を追加していることに注意してください。

トレーニング時には、tfrecordsおよび入力キューに入力パイプラインを使用しました。

_GRAPH = tf.Graph()
with GRAPH.as_default():
    examples, labels = Inputs.read_inputs(CONSTANTS.RecordPaths,
                                          batch_size=CONSTANTS.BATCH_SIZE,
                                          img_shape=CONSTANTS.IMAGE_SHAPE,
                                          num_threads=CONSTANTS.INPUT_PIPELINE_THREADS)
    examples = tf.reshape(examples, [CONSTANTS.BATCH_SIZE, CONSTANTS.IMAGE_SHAPE[0],
                                     CONSTANTS.IMAGE_SHAPE[1], CONSTANTS.IMAGE_SHAPE[2]])
    logits = Vgg3CIFAR10.inference(examples)
    loss = Vgg3CIFAR10.loss(logits, labels)
    OPTIMIZER = tf.train.AdamOptimizer(CONSTANTS.LEARNING_RATE)
_

グラフのロードされた操作で_feed_dict_を使用しようとしています。ただし、現在は単にぶら下がっているだけです。

_MODEL_PATH = 'models/' + CONSTANTS.MODEL_NAME + '.model'

images = tf.placeholder(tf.float32, shape=(1, 32, 32, 3))

def run_inference():
    '''Runs inference against a loaded model'''
    with tf.Session() as sess:
        #sess.run(tf.global_variables_initializer())
        new_saver = tf.train.import_meta_graph(MODEL_PATH + '.meta', clear_devices=True)
        new_saver.restore(sess, MODEL_PATH)
        pred = tf.get_default_graph().get_operation_by_name('prediction')
        Rand = np.random.Rand(1, 32, 32, 3)
        print(Rand)
        print(pred)
        print(sess.run(pred, feed_dict={images: Rand}))
        print('done')

run_inference()
_

元のネットワークはTFRecordsを使用してトレーニングされたため、これは機能していないと思います。サンプルのCIFARデータセットでは、データは小さくなっています。私たちの実際のデータセットは膨大であり、ネットワークをトレーニングするためのデフォルトのベストプラクティスはTFRecordsの理解です。 _feed_dict_は、生産化の観点から非常に完璧な意味を持ちます。いくつかのスレッドをスピンアップし、入力システムからそれを取り込むことができます。

だから私は訓練されたネットワークを持っていると思う、私は予測操作を得ることができます。しかし、入力キューの使用を停止し、_feed_dict_の使用を開始するように指示するにはどうすればよいですか?生産の観点から、私は科学者がそれを作るためにしたことにはアクセスできないことに注意してください。彼らは自分のことをします。そして、標準で合意されたものを使用して、本番環境で使用します。

-------入力OPS --------

tf.Operation 'input/input_producer/Const' type = Const、tf.Operation 'input/input_producer/Size' type = Const、tf.Operation 'input/input_producer/Greater/y' type = Const、tf.Operation 'input/input_producer/Greater 'type = Greater、tf.Operation' input/input_producer/Assert/Const 'type = Const、tf.Operation' input/input_producer/Assert/Assert/data_0 'type = Const、tf.Operation' input/input_producer/Assert/Assert 'type = Assert、tf.Operation' input/input_producer/Identity 'type = Identity、tf.Operation' input/input_producer/RandomShuffle 'type = RandomShuffle、tf.Operation' input/input_producer 'type = FIFOQueueV2、tf。操作「input/input_producer/input_producer_EnqueueMany」type = QueueEnqueueManyV2、tf.Operation「input/input_producer/input_producer_Close」type = QueueCloseV2、tf.Operation「input/input_producer/input_producer_Close_1」type = QueueCloseV2、tf.Operation「input/input_producer/input_producer_Size」 type = QueueSizeV2、tf.Operation 'input/input_producer/Cast' type = Cast、tf.Operation 'input/input_produc er/mul/y 'type = Const、tf.Operation' input/input_producer/mul 'type = Mul、tf.Operation' input/input_producer/fraction_of_32_full/tags 'type = Const、tf.Operation' input/input_producer/fraction_of_32_full ' type = ScalarSummary、tf.Operation 'input/TFRecordReaderV2' type = TFRecordReaderV2、tf.Operation 'input/ReaderReadV2' type = ReaderReadV2

------入力OPSの終了-----

----更新3 ----

TFレコードでトレーニングされたグラフの入力セクションを削除し、最初のレイヤーへの入力を新しい入力に再配線する必要があると思います。手術をするようなものです。しかし、これがTFRecordsを使用して訓練されているように聞こえるほどクレイジーな場合、推論を行うことができる唯一の方法です...

完全なグラフ:

enter image description here

殺すセクション:

enter image description here

質問は次のようになると思います:グラフの入力セクションをどのように強制終了し、それを_feed_dict_に置き換えるのですか?

これへのフォローアップは次のようになります:これは本当にそれを行う正しい方法ですか?これはおかしなようです。

----更新3を終了----

---チェックポイントファイルへのリンク----

https://drcdata.blob.core.windows.net/checkpoints/CIFAR_10_VGG3_50neuron_1pool_1e-3lr_adam.model.zip?st=2017-05-01T21%3A56%3A00Z&se=2020-05-02T21%3A56%3A00Z&sp= rl&sv = 2015-12-11&sr = b&sig = oBCGxlOusB4NOEKnSnD%2FTlRYa5NKNIwAX1IyuZXAr9o%3D

-チェックポイントファイルへのリンクの終了---

-----アップデート4 -----

科学者にモデルをピクルスするだけでモデルのピクルスをつかむことができると仮定して、推論を実行する「通常の」方法を試しました。解凍してから推論を実行します。だから、テストするために、すでに解凍したと仮定して通常の方法を試してみました...豆の価値もありません...

_import tensorflow as tf
import CONSTANTS
import Vgg3CIFAR10
import numpy as np
from scipy import misc
import time

MODEL_PATH = 'models/' + CONSTANTS.MODEL_NAME + '.model'
imgs_bsdir = 'C:/data/cifar_10/train/'

images = tf.placeholder(tf.float32, shape=(1, 32, 32, 3))

logits = Vgg3CIFAR10.inference(images)

def run_inference():
'''Runs inference against a loaded model'''
    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())
        new_saver = tf.train.import_meta_graph(MODEL_PATH + '.meta')#, import_scope='1', input_map={'input:0': images})
        new_saver.restore(sess, MODEL_PATH)
        pred = tf.get_default_graph().get_operation_by_name('prediction')
        enq = sess.graph.get_operation_by_name(enqueue_op)
        #tf.train.start_queue_runners(sess)
        print(Rand)
        print(pred)
        print(enq)
        for i in range(1, 25):
            img = misc.imread(imgs_bsdir + str(i) + '.png').astype(np.float32) / 255.0
            img = img.reshape(1, 32, 32, 3)
            print(sess.run(logits, feed_dict={images : img}))
            time.sleep(3)
        print('done')

run_inference()
_

Tensorflowは、ロードされたモデルからの推論関数を使用して新しいグラフを作成します。次に、他のグラフのすべての他のものをその最後に追加します。そのため、推論を取り戻すことを期待して_feed_dict_を設定すると、ネットワークの最初のパスであるかのように、ランダムなゴミがたくさんあります...

再び;これはナッツのようです。ランダムネットワークをシリアライズおよびデシリアライズするための独自のフレームワークを作成する必要は本当にありますか?これは以前に行われていなければなりませんでした...

-----アップデート4 -----

再び;ありがとう!

19
David Crook

さて、これを理解するには時間がかかりすぎました。だから、ここに世界の残りのための答えがあります。

クイックリマインダー:動的にロードして推論することができるモデルを永続化する必要がありました。

ステップ1:モデルをクラスとして作成し、理想的にはインターフェース定義を使用します

class Vgg3Model:

    NUM_DENSE_NEURONS = 50
    DENSE_RESHAPE = 32 * (CONSTANTS.IMAGE_SHAPE[0] // 2) * (CONSTANTS.IMAGE_SHAPE[1] // 2)

    def inference(self, images):
        '''
        Portion of the compute graph that takes an input and converts it into a Y output
        '''
        with tf.variable_scope('Conv1') as scope:
            C_1_1 = ld.cnn_layer(images, (5, 5, 3, 32), (1, 1, 1, 1), scope, name_postfix='1')
            C_1_2 = ld.cnn_layer(C_1_1, (5, 5, 32, 32), (1, 1, 1, 1), scope, name_postfix='2')
            P_1 = ld.pool_layer(C_1_2, (1, 2, 2, 1), (1, 2, 2, 1), scope)
        with tf.variable_scope('Dense1') as scope:
            P_1 = tf.reshape(P_1, (-1, self.DENSE_RESHAPE))
            dim = P_1.get_shape()[1].value
            D_1 = ld.mlp_layer(P_1, dim, self.NUM_DENSE_NEURONS, scope, act_func=tf.nn.relu)
        with tf.variable_scope('Dense2') as scope:
            D_2 = ld.mlp_layer(D_1, self.NUM_DENSE_NEURONS, CONSTANTS.NUM_CLASSES, scope)
        H = tf.nn.softmax(D_2, name='prediction')
        return H

    def loss(self, logits, labels):
        '''
        Adds Loss to all variables
        '''
        cross_entr = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=logits, labels=labels)
        cross_entr = tf.reduce_mean(cross_entr)
        tf.summary.scalar('cost', cross_entr)
        tf.add_to_collection('losses', cross_entr)
        return tf.add_n(tf.get_collection('losses'), name='total_loss')

ステップ2:任意の入力でネットワークをトレーニングします。私の場合、キューランナーとTFレコードを使用しました。このステップは、モデルの反復、構築、設計、最適化を行う別のチームによって行われることに注意してください。これは時間とともに変化する可能性もあります。更新されたモデルをデバイスに動的にロードできるように、生成される出力はリモートの場所から取得できる必要があります(ハードウェアの再フラッシュは、特に地理的に分散している場合は苦痛です)。この場合には;チームは、グラフセーバーに関連付けられた3つのファイルをドロップします。しかし、そのトレーニングセッションに使用されたモデルの漬物

model = vgg3.Vgg3Model()

def create_sess_ops():
    '''
    Creates and returns operations needed for running
    a tensorflow training session
    '''
    GRAPH = tf.Graph()
    with GRAPH.as_default():
        examples, labels = Inputs.read_inputs(CONSTANTS.RecordPaths,
                                          batch_size=CONSTANTS.BATCH_SIZE,
                                          img_shape=CONSTANTS.IMAGE_SHAPE,
                                          num_threads=CONSTANTS.INPUT_PIPELINE_THREADS)
        examples = tf.reshape(examples, [-1, CONSTANTS.IMAGE_SHAPE[0],
                                     CONSTANTS.IMAGE_SHAPE[1], CONSTANTS.IMAGE_SHAPE[2]], name='infer/input')
        logits = model.inference(examples)
        loss = model.loss(logits, labels)
        OPTIMIZER = tf.train.AdamOptimizer(CONSTANTS.LEARNING_RATE)
        gradients = OPTIMIZER.compute_gradients(loss)
        apply_gradient_op = OPTIMIZER.apply_gradients(gradients)
        gradients_summary(gradients)
        summaries_op = tf.summary.merge_all()
        return [apply_gradient_op, summaries_op, loss, logits], GRAPH

def main():
    '''
    Run and Train CIFAR 10
    '''
    print('starting...')
    ops, GRAPH = create_sess_ops()
    total_duration = 0.0
    with tf.Session(graph=GRAPH) as SESSION:
        COORDINATOR = tf.train.Coordinator()
        THREADS = tf.train.start_queue_runners(SESSION, COORDINATOR)
        SESSION.run(tf.global_variables_initializer())
        SUMMARY_WRITER = tf.summary.FileWriter('Tensorboard/' + CONSTANTS.MODEL_NAME, graph=GRAPH)
        GRAPH_SAVER = tf.train.Saver()

        for Epoch in range(CONSTANTS.EPOCHS):
            duration = 0
            error = 0.0
            start_time = time.time()
            for batch in range(CONSTANTS.MINI_BATCHES):
                _, summaries, cost_val, prediction = SESSION.run(ops)
                error += cost_val
            duration += time.time() - start_time
            total_duration += duration
            SUMMARY_WRITER.add_summary(summaries, Epoch)
            print('Epoch %d: loss = %.2f (%.3f sec)' % (Epoch, error, duration))
            if Epoch == CONSTANTS.EPOCHS - 1 or error < 0.005:
                print(
                'Done training for %d epochs. (%.3f sec)' % (Epoch, total_duration)
            )
                break
        GRAPH_SAVER.save(SESSION, 'models/' + CONSTANTS.MODEL_NAME + '.model')
        with open('models/' + CONSTANTS.MODEL_NAME + '.pkl', 'wb') as output:
            pickle.dump(model, output)
        COORDINATOR.request_stop()
        COORDINATOR.join(THREADS)

ステップ:推論を実行します。漬物モデルをロードします。新しいプレースホルダーをロジットにパイプすることにより、新しいグラフを作成します。その後、セッションの復元を呼び出します。全体のグラフを復元しないでください。変数のみ。

MODEL_PATH = 'models/' + CONSTANTS.MODEL_NAME + '.model'
imgs_bsdir = 'C:/data/cifar_10/train/'

images = tf.placeholder(tf.float32, shape=(1, 32, 32, 3))
with open('models/vgg3.pkl', 'rb') as model_in:
model = pickle.load(model_in)
logits = model.inference(images)

def run_inference():
    '''Runs inference against a loaded model'''
    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())
        new_saver = tf.train.Saver()
        new_saver.restore(sess, MODEL_PATH)
        print("Starting...")
        for i in range(20, 30):
            print(str(i) + '.png')
            img = misc.imread(imgs_bsdir + str(i) + '.png').astype(np.float32) / 255.0
            img = img.reshape(1, 32, 32, 3)
            pred = sess.run(logits, feed_dict={images : img})
            max_node = np.argmax(pred)
            print('predicted label: ' + str(max_node))
        print('done')

run_inference()

インターフェイスを使用してこれを改善する方法は間違いなくあり、おそらくすべてをより適切にパッケージ化できます。しかし、これは機能しており、私たちがどのように前進するかについての段階を設定しています。

FINAL NOTEこれを最終的に本番環境にプッシュしたとき、グラフを作成するためにすべてをバカな `mymodel_model.pyファイルに同梱する必要がありました。そのため、すべてのモデルに命名規則を適用し、実稼働モデルを実行するためのコーディング標準もあるため、これを適切に行うことができます。

がんばろう!

17
David Crook

Model.predict()ほどカットされて乾燥しているわけではありませんが、それでも非常に簡単です。

モデルには、関心のある最終出力を計算するテンソルが必要です。そのテンソルをoutputと名付けましょう。あなたは現在、損失関数を持っているだけかもしれません。その場合、必要な出力を実際に計算する別のテンソル(モデル内の変数)を作成します。

たとえば、損失関数が次の場合:

_tf.nn.sigmoid_cross_entropy_with_logits(last_layer_activation, labels)
_

そして、出力がクラスごとに[0,1]の範囲にあることを期待し、別の変数を作成します

_output = tf.sigmoid(last_layer_activation)
_

さて、sess.run(...)を呼び出すときは、outputテンソルを要求するだけです。通常トレーニングする最適化OPを要求しないでください。この変数を要求すると、テンソルフローは値を生成するために必要な最小限の作業を行います(たとえば、backprop、損失関数、および単純なフィードフォワードパスがoutputを計算するために必要なすべてのことを気にしません。

そのため、モデルの推論を返すサービスを作成する場合、モデルをメモリ/ GPUにロードしたままにして、繰り返します。

_sess.run(output, feed_dict={X: input_data})
_

テンソルフローは、要求している出力を生成するために必要のないopを計算する必要がないため、ラベルを供給する必要はありません。モデルなどを変更する必要はありません。

このアプローチはmodel.predict(...)ほど明白ではないかもしれませんが、私はそれが非常に柔軟であると主張します。もっと複雑なモデルで遊んでいるなら、おそらくこのアプローチを愛することを学ぶでしょう。 model.predict()は「箱の中を考える」ようなものです。

5
David Parks