Tensorflow: восстановление графика и модели, а затем выполнение оценки на одном изображении
Я думаю, что было бы очень полезно сообществу Tensorflow, если бы было хорошо документированное решение решающей задачи тестирования одного нового изображения против модели, созданной convnet в учебнике CIFAR-10.
Возможно, я ошибаюсь, но этот критический шаг, который делает пригодную для обучения на практике модель, кажется, отсутствует. В этом учебнике есть "недостающее звено" - script, которое будет напрямую загружать одно изображение (в виде массива или двоичного кода), сравнивать его с обученной моделью и возвращать классификацию.
Предыдущие ответы дают частичные решения, которые объясняют общий подход, но ни один из которых я не смог успешно реализовать. Другие кусочки можно найти здесь и там, но, к сожалению, не добавили рабочего решения. Просьба рассмотреть сделанное мной исследование, прежде чем пометить это как дублирующее или уже ответившее.
Tensorflow: как сохранить/восстановить модель?
Восстановление модели TensorFlow
Невозможно восстановить модели в tensorflow v0.8
https://gist.github.com/nikitakit/6ef3b72be67b86cb7868
Самый популярный ответ - первый, в котором @RyanSepassi и @YaroslavBulatov описывают проблему и подход: нужно "вручную построить график с идентичными именами node и использовать Saver для загрузки в него весов", Хотя оба ответа полезны, неясно, как можно было бы подключить его к проекту CIFAR-10.
Полностью функциональное решение было бы очень желательно, поэтому мы могли бы перенести его на другие проблемы классификации изображений. В этом отношении есть несколько вопросов, касающихся SO, которые требуют этого, но до сих пор нет полного ответа (например Загрузить контрольную точку и оценить одиночное изображение с DNN тензорного потока).
Надеюсь, мы сможем сблизиться с рабочим script, который каждый мог бы использовать.
Ниже script еще не функционирует, и я был бы рад услышать от вас, как это можно улучшить, чтобы обеспечить решение для классификации одного изображения с использованием обучаемой модели учебника 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()
Ответы
Ответ 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.
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)
Ответ 2
Есть два метода для подачи одного нового изображения в 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)
script требует, чтобы пользователь создал два заполнителя и условный оператор выполнения, чтобы он работал.
Заполнители и оператор условного выполнения добавляются в 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 подключаются к объекту очереди, который представляет собой многоэтапную очередь, которая может предварительно отбирать данные из файлов параллельно. См. Приятную анимацию очереди queer здесь
В то время как бегуны в очереди эффективны в предварительной выборке большого набора данных для обучения, они являются излишним для вывода или тестирования, где нужно классифицировать только один файл, а также они немного участвуют в изменении/обслуживании.
По этой причине я добавил местозаполнитель "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})
Другой placeholder "imgs" содержит тензор формы (1,32,32,3) для изображения, которое будет подаваться во время вывода - первое измерение представляет собой размер партии, который является одним в этом случае. Я изменил модель cifar, чтобы принимать 32x32 изображения вместо 24x24, поскольку исходные изображения cifar10 - 32x32.
Наконец, условный оператор подает вывод на столбец или счетчик очереди. Заполнитель "is_training" установлен на False во время вывода, а "img" заполнитель получает массив numpy - массив numpy преобразуется из 3-х мерного вектора в соответствие входному тензору с функцией вывода в модели.
Вот и все. Любая модель может быть выведена с помощью отдельных/пользовательских тестовых данных, как показано в script выше. По существу читайте график, подавайте данные в узлы графа и запускайте график, чтобы получить окончательный результат.
Теперь второй метод. Другой подход - взломать cifar10.py и cifar10_eval.py, чтобы изменить размер партии на один и заменить данные, поступающие из очереди, с одной, прочитанной из файла.
Установите размер партии в 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))
Для запуска этого метода вывода нет отдельного script, просто запустите cifar10_eval.py, который теперь будет читать файл из пользовательского местоположения с размером партии в один.
Ответ 3
получил работу с этим
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]]
Ответ 4
У меня нет рабочего кода для вас, я боюсь, но здесь, как мы часто решаем эту проблему в производстве:
-
Сохраните GraphDef на диск, используя что-то вроде write_graph.
-
Используйте freeze_graph для загрузки GraphDef и контрольных точек и сохраните GraphDef с переменными, преобразованными в константы.
-
Загрузите GraphDef в нечто вроде label_image или classify_image.
Для вашего примера это слишком много, но я бы хотя бы предложил сериализовать график в исходном примере как GraphDef, а затем загрузить его в ваш script (так что вам не нужно дублировать код, генерирующий график). С тем же графиком, который вы создали, вы сможете заполнить его из SaverDef, а в качестве примера может помочь файл freeze_graph script.