CIFAR-10チュートリアルのconvnet によって作成されたモデルに対して単一の新しいイメージをテストするという重要なタスクに対する十分に文書化されたソリューションがあれば、Tensorflowコミュニティにとって非常に役立つと思います。
私は間違っているかもしれませんが、訓練されたモデルを実際に使用できるようにするこの重要なステップは欠けているようです。このチュートリアルには「ミッシングリンク」があります。単一の画像を(配列またはバイナリとして)直接読み込み、トレーニング済みのモデルと比較し、分類を返すスクリプトです。
以前の回答は、全体的なアプローチを説明する部分的な解決策を提供しますが、どれもうまく実装できませんでした。他の部分はあちこちで見つけることができますが、残念ながら実用的なソリューションに追加されていません。これを重複または既に回答済みとしてタグ付けする前に、私が行った研究をご検討ください。
https://Gist.github.com/nikitakit/6ef3b72be67b86cb7868
最も一般的な答えは最初のもので、@ RyanSepassiと@YaroslavBulatovが問題とアプローチを説明します。「同じノード名でグラフを手動で構築し、Saverを使用して重みをロードする」必要があります。両方の答えは役立ちますが、これをCIFAR-10プロジェクトにどのように組み込むかは明らかではありません。
完全に機能するソリューションが非常に望ましいので、他の単一画像分類の問題に移植できます。これに関しては、SOにいくつかの質問がありますが、まだ完全な回答はありません(たとえば、 チェックポイントを読み込んで、Tensorflow DNNで単一のイメージを評価する )。
誰でも使用できる実用的なスクリプトに収束できることを願っています。
以下のスクリプトはまだ機能していません。CIFAR-10TFチュートリアルトレーニングモデルを使用して、単一画像分類のソリューションを提供するためにこれをどのように改善できるかについて、お気軽にお問い合わせください。
すべての変数、ファイル名などが元のチュートリアルから変更されていないと仮定します。
新しいファイル:cifar10_eval_single.py
import cv2
import tensorflow as tf
FLAGS = tf.app.flags.FLAGS
tf.app.flags.DEFINE_string('eval_dir', './input/eval',
"""Directory where to write event logs.""")
tf.app.flags.DEFINE_string('checkpoint_dir', './input/train',
"""Directory where to read model checkpoints.""")
def get_single_img():
file_path = './input/data/single/test_image.tif'
pixels = cv2.imread(file_path, 0)
return pixels
def eval_single_img():
# below code adapted from @RyanSepassi, however not functional
# among other errors, saver throws an error that there are no
# variables to save
with tf.Graph().as_default():
# Get image.
image = get_single_img()
# Build a Graph.
# TODO
# Create dummy variables.
x = tf.placeholder(tf.float32)
w = tf.Variable(tf.zeros([1, 1], dtype=tf.float32))
b = tf.Variable(tf.ones([1, 1], dtype=tf.float32))
y_hat = tf.add(b, tf.matmul(x, w))
saver = tf.train.Saver()
with tf.Session() as sess:
sess.run(tf.initialize_all_variables())
ckpt = tf.train.get_checkpoint_state(FLAGS.checkpoint_dir)
if ckpt and ckpt.model_checkpoint_path:
saver.restore(sess, ckpt.model_checkpoint_path)
print('Checkpoint found')
else:
print('No checkpoint found')
# Run the model to get predictions
predictions = sess.run(y_hat, feed_dict={x: image})
print(predictions)
def main(argv=None):
if tf.gfile.Exists(FLAGS.eval_dir):
tf.gfile.DeleteRecursively(FLAGS.eval_dir)
tf.gfile.MakeDirs(FLAGS.eval_dir)
eval_single_img()
if __== '__main__':
tf.app.run()
単一の新しい画像を cifar1 モデルにフィードするには、2つの方法があります。最初の方法はよりクリーンなアプローチですが、メインファイルを変更する必要があるため、再トレーニングが必要になります。 2番目の方法は、ユーザーがモデルファイルを変更するのではなく、既存のチェックポイント/メタグラフファイルを使用する場合に適用できます。
最初のアプローチのコードは次のとおりです。
import tensorflow as tf
import numpy as np
import cv2
sess = tf.Session('', tf.Graph())
with sess.graph.as_default():
# Read meta graph and checkpoint to restore tf session
saver = tf.train.import_meta_graph("/tmp/cifar10_train/model.ckpt-200.meta")
saver.restore(sess, "/tmp/cifar10_train/model.ckpt-200")
# Read a single image from a file.
img = cv2.imread('tmp.png')
img = np.expand_dims(img, axis=0)
# Start the queue runners. If they are not started the program will hang
# see e.g. https://www.tensorflow.org/programmers_guide/reading_data
coord = tf.train.Coordinator()
threads = []
for qr in sess.graph.get_collection(tf.GraphKeys.QUEUE_RUNNERS):
threads.extend(qr.create_threads(sess, coord=coord, daemon=True,
start=True))
# In the graph created above, feed "is_training" and "imgs" placeholders.
# Feeding them will disconnect the path from queue runners to the graph
# and enable a path from the placeholder instead. The "img" placeholder will be
# fed with the image that was read above.
logits = sess.run('softmax_linear/softmax_linear:0',
feed_dict={'is_training:0': False, 'imgs:0': img})
#Print classifiction results.
print(logits)
このスクリプトでは、ユーザーが2つのプレースホルダーと条件付き実行ステートメントを作成して機能させる必要があります。
以下に示すように、プレースホルダーと条件付き実行ステートメントがcifar10_train.pyに追加されます。
def train():
"""Train CIFAR-10 for a number of steps."""
with tf.Graph().as_default():
global_step = tf.contrib.framework.get_or_create_global_step()
with tf.device('/cpu:0'):
images, labels = cifar10.distorted_inputs()
is_training = tf.placeholder(dtype=bool,shape=(),name='is_training')
imgs = tf.placeholder(tf.float32, (1, 32, 32, 3), name='imgs')
images = tf.cond(is_training, lambda:images, lambda:imgs)
logits = cifar10.inference(images)
Cifar10モデルの入力は、ファイルからデータを並行してプリフェッチできる多段キューであるキューランナーオブジェクトに接続されています。キューランナーの素敵なアニメーションを見る here
キューランナーは、トレーニングのために大きなデータセットをプリフェッチするのに効率的ですが、単一のファイルのみを分類する必要がある推論/テストには過剰であり、変更/維持にはもう少し関与します。そのため、以下に示すように、トレーニング中にFalseに設定されるプレースホルダー「is_training」を追加しました。
import numpy as np
tmp_img = np.ndarray(shape=(1,32,32,3), dtype=float)
with tf.train.MonitoredTrainingSession(
checkpoint_dir=FLAGS.train_dir,
hooks=[tf.train.StopAtStepHook(last_step=FLAGS.max_steps),
tf.train.NanTensorHook(loss),
_LoggerHook()],
config=tf.ConfigProto(
log_device_placement=FLAGS.log_device_placement)) as mon_sess:
while not mon_sess.should_stop():
mon_sess.run(train_op, feed_dict={is_training: True, imgs: tmp_img})
別のプレースホルダー「imgs」は、推論中にフィードされる画像の形状のテンソル(1,32,32,3)を保持します。最初の次元は、この場合は1つのバッチサイズです。元のcifar10イメージは32x32であるため、24x24ではなく32x32イメージを受け入れるようにcifarモデルを変更しました。
最後に、条件ステートメントは、プレースホルダーまたはキューランナーの出力をグラフに送ります。 「is_training」プレースホルダーは推論中にFalseに設定され、「img」プレースホルダーにはnumpy配列が入力されます。numpy配列は3次元から4次元ベクトルに変形され、入力テンソルがモデルの推論関数に適合します。
これですべてです。上記のスクリプトのように、単一/ユーザー定義のテストデータで任意のモデルを推測できます。基本的にグラフを読み取り、データをグラフノードにフィードし、グラフを実行して最終出力を取得します。
次に、2番目の方法。もう1つの方法は、cifar10.pyとcifar10_eval.pyをハックしてバッチサイズを1に変更し、キューランナーからのデータをファイルから読み取ったデータに置き換えることです。
バッチサイズを1に設定します。
tf.app.flags.DEFINE_integer('batch_size', 1,
"""Number of images to process in a batch.""")
画像ファイルを読み込んで推論を呼び出します。
def evaluate(): with tf.Graph().as_default() as g:
# Get images and labels for CIFAR-10.
eval_data = FLAGS.eval_data == 'test'
images, labels = cifar10.inputs(eval_data=eval_data)
import cv2
img = cv2.imread('tmp.png')
img = np.expand_dims(img, axis=0)
img = tf.cast(img, tf.float32)
logits = cifar10.inference(img)
次に、ロジットをeval_onceに渡し、evalを一度変更してロジットを評価します。
def eval_once(saver, summary_writer, top_k_op, logits, summary_op):
...
while step < num_iter and not coord.should_stop():
predictions = sess.run([top_k_op])
print(sess.run(logits))
この推論方法を実行するための別個のスクリプトはありません。cifar10_eval.pyを実行するだけで、バッチサイズが1のユーザー定義の場所からファイルを読み取ります。
これが、一度に1つのイメージを実行した方法です。スコープを取得することの再利用には少々ハッキングがあるように思えます。
これはヘルパー関数です
def restore_vars(saver, sess, chkpt_dir):
""" Restore saved net, global score and step, and epsilons OR
create checkpoint directory for later storage. """
sess.run(tf.initialize_all_variables())
checkpoint_dir = chkpt_dir
if not os.path.exists(checkpoint_dir):
try:
os.makedirs(checkpoint_dir)
except OSError:
pass
path = tf.train.get_checkpoint_state(checkpoint_dir)
#print("path1 = ",path)
#path = tf.train.latest_checkpoint(checkpoint_dir)
print(checkpoint_dir,"path = ",path)
if path is None:
return False
else:
saver.restore(sess, path.model_checkpoint_path)
return True
Forループ内で一度に1つのイメージを実行するコードの主要部分を次に示します。
to_restore = True
with tf.Session() as sess:
for i in test_img_idx_set:
# Gets the image
images = get_image(i)
images = np.asarray(images,dtype=np.float32)
images = tf.convert_to_tensor(images/255.0)
# resize image to whatever you're model takes in
images = tf.image.resize_images(images,256,256)
images = tf.reshape(images,(1,256,256,3))
images = tf.cast(images, tf.float32)
saver = tf.train.Saver(max_to_keep=5, keep_checkpoint_every_n_hours=1)
#print("infer")
with tf.variable_scope(tf.get_variable_scope()) as scope:
if to_restore:
logits = inference(images)
else:
scope.reuse_variables()
logits = inference(images)
if to_restore:
restored = restore_vars(saver, sess,FLAGS.train_dir)
print("restored ",restored)
to_restore = False
logit_val = sess.run(logits)
print(logit_val)
ここにプレースホルダーを使用した上記の代替実装がありますが、私の意見では少しきれいです。ただし、歴史的な理由から上記の例を残します。
imgs_place = tf.placeholder(tf.float32, shape=[my_img_shape_put_here])
images = tf.reshape(imgs_place,(1,256,256,3))
saver = tf.train.Saver(max_to_keep=5, keep_checkpoint_every_n_hours=1)
#print("infer")
logits = inference(images)
restored = restore_vars(saver, sess,FLAGS.train_dir)
print("restored ",restored)
with tf.Session() as sess:
for i in test_img_idx_set:
logit_val = sess.run(logits,feed_dict={imgs_place=i})
print(logit_val)
これで動作しました
softmax = gn.inference(image)
saver = tf.train.Saver()
ckpt = tf.train.get_checkpoint_state(FLAGS.checkpoint_dir)
with tf.Session() as sess:
saver.restore(sess, ckpt.model_checkpoint_path)
softmaxval = sess.run(softmax)
print(softmaxval)
出力
[[ 6.73550041e-03 4.44930716e-04 9.92570221e-01 1.00681427e-06
3.05406687e-08 2.38927707e-04 1.89839399e-12 9.36238484e-06
1.51646684e-09 3.38977535e-09]]
私はあなたのために働くコードを持っていないのではないかと心配していますが、私たちが本番環境でこの問題にしばしば取り組む方法を以下に示します。
write_graph のようなものを使用して、GraphDefをディスクに保存します。
freeze_graph を使用してGraphDefとチェックポイントをロードし、変数を定数に変換してGraphDefを保存します。
label_image または classify_image のようなものでGraphDefをロードします。
あなたの例ではこれはやり過ぎですが、少なくとも元の例のグラフをGraphDefとしてシリアル化し、それをスクリプトにロードすることをお勧めします(グラフを生成するコードを複製する必要はありません)。同じグラフを作成すると、SaverDefからグラフを作成できるはずです。その例として、freeze_graphスクリプトが役立つ場合があります。