Python Tensorflow:恢复图形和模型,然后对单个图像进行评估
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/37649060/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Tensorflow: restoring a graph and model then running evaluation on a single image
提问by pepe
I think it would be immensely helpful to the Tensorflow community if there was a well-documented solution to the crucial task of testing a single new image against the model created by the convnet in the CIFAR-10 tutorial.
我认为,如果针对CIFAR-10 教程中的 convnet创建的模型测试单个新图像的关键任务有一个有据可查的解决方案,那么这对 Tensorflow 社区将非常有帮助。
I may be wrong, but this critical step that makes the trained model usable in practice seems to be lacking. There is a "missing link" in that tutorial—a script that would directly load a single image (as array or binary), compare it against the trained model, and return a classification.
我可能错了,但似乎缺乏使训练后的模型在实践中可用的关键步骤。该教程中有一个“缺失链接”——一个直接加载单个图像(作为数组或二进制)的脚本,将其与训练好的模型进行比较,并返回一个分类。
Prior answers give partial solutions that explain the overall approach, but none of which I've been able to implement successfully. Other bits and pieces can be found here and there, but unfortunately haven't added up to a working solution. Kindly consider the research I've done, before tagging this as duplicate or already answered.
先前的答案给出了解释整体方法的部分解决方案,但我都无法成功实施。可以在这里和那里找到其他零碎的东西,但不幸的是还没有形成一个有效的解决方案。在将其标记为重复或已经回答之前,请考虑我所做的研究。
Tensorflow: how to save/restore a model?
Unable to restore models in tensorflow v0.8
https://gist.github.com/nikitakit/6ef3b72be67b86cb7868
https://gist.github.com/nikitakit/6ef3b72be67b86cb7868
The most popular answer is the first, in which @RyanSepassi and @YaroslavBulatov describe the problem and an approach: one needs to "manually construct a graph with identical node names, and use Saver to load the weights into it". Although both answers are helpful, it is not apparent how one would go about plugging this into the CIFAR-10 project.
最受欢迎的答案是第一个,其中@RyanSepassi 和@YaroslavBulatov 描述了问题和方法:需要“手动构建具有相同节点名称的图,并使用 Saver 将权重加载到其中”。尽管这两个答案都有帮助,但不清楚如何将其插入 CIFAR-10 项目。
A fully functional solution would be highly desirable so we could port it to other single image classification problems. There are several questions on SO in this regard that ask for this, but still no full answer (for example Load checkpoint and evaluate single image with tensorflow DNN).
一个功能齐全的解决方案将是非常可取的,因此我们可以将其移植到其他单图像分类问题中。在这方面有几个关于 SO 的问题,但仍然没有完整的答案(例如Load checkpoint and evaluation single image with tensorflow DNN)。
I hope we can converge on a working script that everyone could use.
我希望我们可以集中在一个每个人都可以使用的工作脚本上。
The below script is not yet functional, and I'd be happy to hear from you on how this can be improved to provide a solution for single-image classification using the CIFAR-10 TF tutorial trained model.
下面的脚本还不能正常运行,我很高兴收到您关于如何改进它以使用 CIFAR-10 TF 教程训练模型为单图像分类提供解决方案的意见。
Assume all variables, file names etc. are untouched from the original tutorial.
假设原始教程中的所有变量、文件名等都未受影响。
New file: cifar10_eval_single.py
新文件: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 __name__ == '__main__':
tf.app.run()
回答by bigdata2
There are two methods to feed a single new image to the cifar10model. The first method is a cleaner approach but requires modification in the main file, hence will require retraining. The second method is applicable when a user does not want to modify the model files and instead wants to use the existing check-point/meta-graph files.
有两种方法可以将单个新图像提供给cifar10模型。第一种方法是一种更简洁的方法,但需要在主文件中进行修改,因此需要重新训练。当用户不想修改模型文件而是想使用现有的检查点/元图文件时,第二种方法适用。
The code for the first approach is as follows:
第一种方法的代码如下:
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)
The script requires that a user creates two placeholders and a conditional execution statement for it to work.
该脚本要求用户创建两个占位符和一个条件执行语句以使其工作。
The placeholders and conditional execution statement are added in cifar10_train.py as shown below:
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)
The inputs in cifar10 model are connected to queue runner object which is a multistage queue that can prefetch data from files in parallel. See a nice animation of queue runner here
cifar10 模型中的输入连接到队列运行器对象,该对象是一个多级队列,可以并行地从文件中预取数据。在此处查看队列运行器的精美动画
While queue runners are efficient in prefetching large dataset for training, they are an overkill for inference/testing where only a single file is needed to be classified, also they are a bit more involved to modify/maintain. For that reason, I have added a placeholder "is_training", which is set to False while training as shown below:
虽然队列运行器在预取用于训练的大型数据集方面很有效,但它们对于只需要对单个文件进行分类的推理/测试来说是一种矫枉过正,而且它们需要更多地修改/维护。出于这个原因,我添加了一个占位符“is_training”,它在训练时设置为 False,如下所示:
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})
Another placeholder "imgs" holds a tensor of shape (1,32,32,3) for the image that will be fed during inference -- the first dimension is the batch size which is one in this case. I have modified cifar model to accept 32x32 images instead of 24x24 as the original cifar10 images are 32x32.
另一个占位符“imgs”为将在推理过程中输入的图像保存一个形状为 (1,32,32,3) 的张量——第一个维度是批量大小,在这种情况下是一个。我修改了 cifar 模型以接受 32x32 图像而不是 24x24,因为原始 cifar10 图像是 32x32。
Finally, the conditional statement feeds the placeholder or queue runner output to the graph. The "is_training" placeholder is set to False during inference and "img" placeholder is fed a numpy array -- the numpy array is reshaped from 3 to 4 dimensional vector to conform to the input tensor to inference function in the model.
最后,条件语句将占位符或队列运行器输出提供给图形。在推理过程中,“is_training”占位符设置为 False,而“img”占位符被输入一个 numpy 数组——numpy 数组从 3 维向量改成 4 维向量,以符合模型中推理函数的输入张量。
That is all there is to it. Any model can be inferred with a single/user defined test data like shown in the script above. Essentially read the graph, feed data to the graph nodes and run the graph to get the final output.
这就是它的全部内容。可以使用单个/用户定义的测试数据推断任何模型,如上面的脚本所示。本质上是读取图形,将数据提供给图形节点并运行图形以获得最终输出。
Now the second method. The other approach is to hack cifar10.py and cifar10_eval.py to change batch size to one and replace the data coming from the queue runner with the one read from a file.
现在第二种方法。另一种方法是破解 cifar10.py 和 cifar10_eval.py 以将批量大小更改为 1,并将来自队列运行器的数据替换为从文件中读取的数据。
Set batch size to 1:
将批量大小设置为 1:
tf.app.flags.DEFINE_integer('batch_size', 1,
"""Number of images to process in a batch.""")
Call inference with an image file read.
读取图像文件进行调用推理。
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)
Then pass logits to eval_once and modify eval once to evaluate logits:
然后将 logits 传递给 eval_once 并修改 eval once 以评估 logits:
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))
There is no separate script to run this method of inference, just run cifar10_eval.py which will now read a file from the user defined location with a batch size of one.
没有单独的脚本来运行这种推理方法,只需运行 cifar10_eval.py,它现在将从用户定义的位置读取一个批处理大小为 1 的文件。
回答by Steven
Here's how I ran a single image at a time. I'll admit it seems a bit hacky with the reuse of getting the scope.
这是我一次运行单个图像的方式。我承认重用获取范围似乎有点麻烦。
This is a helper function
这是一个辅助函数
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
Here is the main part of the code that runs a single image at a time within the for loop.
这是在 for 循环中一次运行单个图像的代码的主要部分。
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)
Here is an alternative implementation to the above using place holders it's a bit cleaner in my opinion. but I'll leave the above example for historical reasons.
这是上述使用占位符的替代实现,在我看来它更清晰。但出于历史原因,我会离开上面的例子。
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)
回答by Noah Smith
got it working with this
让它与这个一起工作
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)
output
输出
[[ 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]]
回答by Pete Warden
I don't have working code for you I'm afraid, but here's how we often tackle this problem in production:
恐怕我没有适合您的工作代码,但我们通常在生产中解决此问题的方法如下:
Save out the GraphDef to disk, using something like write_graph.
Use freeze_graphto load the GraphDef and checkpoints, and save out a GraphDef with the Variables converted into Constants.
Load the GraphDef in something like label_imageor classify_image.
使用类似write_graph 的东西将 GraphDef 保存到磁盘。
使用freeze_graph加载 GraphDef 和检查点,并保存一个 GraphDef 并将变量转换为常量。
加载GraphDef在像label_image或classify_image。
For your example this is overkill, but I would at least suggest serializing the graph in the original example as a GraphDef, and then loading it in your script (so you don't have to duplicate the code generating the graph). With the same graph created, you should be able to populate it from a SaverDef, and the freeze_graph script may help as an example.
对于您的示例,这有点矫枉过正,但我至少建议将原始示例中的图形序列化为 GraphDef,然后将其加载到您的脚本中(这样您就不必复制生成图形的代码)。创建相同的图形后,您应该能够从 SaverDef 填充它,作为示例, freeze_graph 脚本可能会有所帮助。