pep*_*epe 37 python machine-learning python-3.x tensorflow
我认为如果有针对CIFAR-10教程中由convnet创建的模型测试单个新图像的关键任务的详细解决方案,那么Tensorflow社区将会非常有用.
我可能错了,但是这个让训练有素的模型在实践中可用的关键步骤似乎缺乏.该教程中有一个"缺失的链接" - 一个直接加载单个图像(作为数组或二进制文件)的脚本,将其与训练模型进行比较,然后返回分类.
先前的答案提供了解释整体方法的部分解决方案,但我没有能够成功实施.其他零碎可以在这里和那里找到,但遗憾的是还没有添加到一个可行的解决方案.在将此标记为重复或已经回答之前,请考虑我已完成的研究.
https://gist.github.com/nikitakit/6ef3b72be67b86cb7868
最流行的答案是第一个,其中@RyanSepassi和@YaroslavBulatov描述了问题和方法:人们需要"手动构建具有相同节点名称的图形,并使用Saver将权重加载到其中".虽然这两个答案都很有帮助,但是如何将其插入CIFAR-10项目并不明显.
一个功能齐全的解决方案非常需要,因此我们可以将其移植到其他单个图像分类问题中.在这方面SO有几个问题要求这个,但仍然没有完整的答案(例如加载检查点并评估具有张量流DNN的单个图像).
我希望我们能够集中在每个人都可以使用的工作脚本上.
以下脚本尚未正常运行,我很高兴听到您如何改进,以便使用CIFAR-10 TF教程训练模型为单图像分类提供解决方案.
假设所有变量,文件名等都不受原始教程的影响.
新文件: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()
Run Code Online (Sandbox Code Playgroud)
big*_*ta2 10
有两种方法可以将单个新图像提供给cifar10模型.第一种方法是更简洁的方法,但需要在主文件中进行修改,因此需要重新训练.当用户不想修改模型文件而是想要使用现有的检查点/元图文件时,第二种方法适用.
第一种方法的代码如下:
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)
Run Code Online (Sandbox Code Playgroud)
该脚本要求用户创建两个占位符和条件执行语句以使其起作用.
占位符和条件执行语句添加在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)
Run Code Online (Sandbox Code Playgroud)
cifar10模型中的输入连接到队列运行器对象,该对象是一个多级队列,可以并行从文件中预取数据.在这里查看一个漂亮的队列运动员动画
虽然队列运行器在预取用于训练的大型数据集方面是有效的,但它们对于推理/测试来说是过度的,其中仅需要对单个文件进行分类,并且它们更多地涉及修改/维护.出于这个原因,我添加了一个占位符"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})
Run Code Online (Sandbox Code Playgroud)
另一个占位符"imgs"对于将在推理期间馈送的图像保持形状的张量(1,32,32,3) - 第一个维度是批量大小,在这种情况下是一个.我修改了cifar模型以接受32x32图像而不是24x24,因为原始cifar10图像是32x32.
最后,条件语句将占位符或队列运行器输出提供给图形.在推理期间,"is_training"占位符被设置为False,并且"img"占位符被馈送到numpy数组中 - numpy数组从3到4维向量重新形成以符合模型中的输入张量到推理函数.
这就是它的全部.任何模型都可以使用单个/用户定义的测试数据推断,如上面的脚本所示.基本上读取图形,将数据提供给图形节点并运行图形以获得最终输出.
现在是第二种方法.另一种方法是破解cifar10.py和cifar10_eval.py以将批量大小更改为1并将来自队列运行程序的数据替换为从文件中读取的数据.
将批量大小设置为1:
tf.app.flags.DEFINE_integer('batch_size', 1,
"""Number of images to process in a batch.""")
Run Code Online (Sandbox Code Playgroud)
使用读取的图像文件调用推理.
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)
Run Code Online (Sandbox Code Playgroud)
然后将logits传递给eval_once并修改eval一次以评估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))
Run Code Online (Sandbox Code Playgroud)
没有单独的脚本来运行这种推理方法,只需运行cifar10_eval.py,它现在将从批量大小为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
Run Code Online (Sandbox Code Playgroud)
以下是在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)
Run Code Online (Sandbox Code Playgroud)
以下是使用占位符的上述替代实现,在我看来它更清晰一些.但出于历史原因,我将离开上面的例子.
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)
Run Code Online (Sandbox Code Playgroud)
恐怕我没有适合您的工作代码,但我们通常在生产中解决此问题的方法如下:
使用write_graph之类的方法将 GraphDef 保存到磁盘。
使用freeze_graph加载 GraphDef 和检查点,并保存一个将变量转换为常量的 GraphDef。
将 GraphDef 加载为label_image或classify_image之类的内容。
对于您的示例,这有点过分了,但我至少建议将原始示例中的图形序列化为 GraphDef,然后将其加载到脚本中(这样您就不必重复生成图形的代码)。创建相同的图表后,您应该能够从 SaverDef 填充它,并且 freeze_graph 脚本可能会有所帮助作为示例。