Tensorflow:恢复图形和模型,然后在单个图像上运行评估

pep*_*epe 37 python machine-learning python-3.x tensorflow

我认为如果有针对CIFAR-10教程中由convnet创建的模型测试单个新图像的关键任务的详细解决方案,那么Tensorflow社区将会非常有用.

我可能错了,但是这个让训练有素的模型在实践中可用的关键步骤似乎缺乏.该教程中有一个"缺失的链接" - 一个直接加载单个图像(作为数组或二进制文件)的脚本,将其与训练模型进行比较,然后返回分类.

先前的答案提供了解释整体方法的部分解决方案,但我没有能够成功实施.其他零碎可以在这里和那里找到,但遗憾的是还没有添加到一个可行的解决方案.在将此标记为重复或已经回答之前,请考虑我已完成的研究.

Tensorflow:如何保存/恢复模型?

恢复TensorFlow模型

无法在tensorflow v0.8中恢复模型

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的用户定义位置读取文件.


Ste*_*ven 6

这是我一次运行单个图像的方式.我承认,重新获得范围似乎有些苛刻.

这是一个辅助函数

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)


Pet*_*den 2

恐怕我没有适合您的工作代码,但我们通常在生产中解决此问题的方法如下:

对于您的示例,这有点过分了,但我至少建议将原始示例中的图形序列化为 GraphDef,然后将其加载到脚本中(这样您就不必重复生成图形的代码)。创建相同的图表后,您应该能够从 SaverDef 填充它,并且 freeze_graph 脚本可能会有所帮助作为示例。