| import os |
| import re |
| import numpy as np |
| from multiprocessing import Process |
| |
|
|
| import tensorflow as tf |
| from tensorflow.python.client import timeline |
| import tensorflow.contrib.slim as slim |
|
|
| from . import util |
| from ..ops import forward_warp |
| from .image_warp import image_warp |
| from .unsupervised import unsupervised_loss |
| from .supervised import supervised_loss |
| from .losses import occlusion, DISOCC_THRESH, create_outgoing_mask |
| from .flow_util import flow_error_avg, flow_to_color, flow_error_image, outlier_pct |
| from ..gui import display |
| from .util import summarized_placeholder |
| from .input import resize_input, resize_output_crop, resize_output, resize_output_flow |
|
|
|
|
| def restore_networks(sess, params, ckpt, ckpt_path=None): |
| finetune = params.get('finetune', []) |
| train_all = params.get('train_all', None) |
| spec = params.get('flownet', 'S') |
| flownet_num = len(spec) |
|
|
| net_names = ['flownet_c'] + ['stack_{}_flownet'.format(i+1) for i in range(flownet_num - 1)] |
| assert len(finetune) <= flownet_num |
| |
| if train_all: |
| restore_external_nets = finetune if ckpt is None else [] |
| variables_to_save = slim.get_variables_to_restore(include=net_names) |
| else: |
| restore_external_nets = finetune if ckpt is None else finetune[:flownet_num - 1] |
| variables_to_save = slim.get_variables_to_restore(include=[net_names[-1]]) |
|
|
| saver = tf.train.Saver(variables_to_save, max_to_keep=1000) |
|
|
| sess.run(tf.global_variables_initializer()) |
|
|
| if ckpt is not None: |
| |
| saver.restore(sess, ckpt.model_checkpoint_path) |
| saver.recover_last_checkpoints(ckpt.all_model_checkpoint_paths) |
|
|
| for i, ckpt in enumerate(restore_external_nets): |
| print('-- restore', net_names[i], ckpt.model_checkpoint_path) |
| try: |
| nets_to_restore = [net_names[i]] |
| variables_to_restore = slim.get_variables_to_restore( |
| include=nets_to_restore) |
| restorer = tf.train.Saver(variables_to_restore) |
| restorer.restore(sess, ckpt.model_checkpoint_path) |
| except: |
| |
| nets_to_restore = [net_names[i]] |
| variables_to_restore = slim.get_variables_to_restore( |
| include=nets_to_restore) |
| variables_to_restore = [v for v in variables_to_restore |
| if not 'full_res' in v.name] |
| restorer = tf.train.Saver(variables_to_restore) |
| restorer.restore(sess, ckpt.model_checkpoint_path) |
| return saver |
|
|
|
|
| def _add_loss_summaries(): |
| losses = tf.get_collection('losses') |
| for l in losses: |
| tensor_name = re.sub('tower_[0-9]*/', '', l.op.name) |
| tf.summary.scalar(tensor_name, l) |
|
|
|
|
| def _add_param_summaries(): |
| params = tf.get_collection('params') |
| for p in params: |
| tensor_name = re.sub('tower_[0-9]*/', '', p.op.name) |
| tf.summary.scalar(tensor_name, p) |
|
|
|
|
| def _add_image_summaries(): |
| images = tf.get_collection('train_images') |
| for im in images: |
| tensor_name = re.sub('tower_[0-9]*/', '', im.op.name) |
| tf.summary.image(tensor_name, im) |
|
|
|
|
| def _eval_plot(results, image_names, title): |
| import matplotlib.pyplot as plt |
| display(results, image_names, title) |
|
|
|
|
| class Trainer(): |
| def __init__(self, train_batch_fn, eval_batch_fn, params, |
| train_summaries_dir, eval_summaries_dir, ckpt_dir, |
| normalization, debug=False, experiment="", interactive_plot=False, |
| supervised=False, devices=None): |
|
|
| self.train_summaries_dir = train_summaries_dir |
| self.eval_summaries_dir = eval_summaries_dir |
| self.ckpt_dir = ckpt_dir |
| self.params = params |
| self.debug = debug |
| self.train_batch_fn = train_batch_fn |
| self.eval_batch_fn = eval_batch_fn |
| self.normalization = normalization |
| self.experiment = experiment |
| self.interactive_plot = interactive_plot |
| self.plot_proc = None |
| self.supervised = supervised |
| self.loss_fn = supervised_loss if supervised else unsupervised_loss |
| self.devices = devices or '/gpu:0' |
| self.shared_device = devices[0] if len(devices) == 1 else '/cpu:0' |
|
|
| def run(self, min_iter, max_iter): |
| """Train (at most) from min_iter + 1 to max_iter. |
| If checkpoints are found in ckpt_dir, |
| they must be have a global_step within [min_iter, max_iter]. In this case, |
| training is continued from global_step + 1 until max_iter is reached. |
| """ |
| save_interval = self.params['save_interval'] |
|
|
| ckpt = tf.train.get_checkpoint_state(self.ckpt_dir) |
| if ckpt is not None: |
| ckpt_path = ckpt.model_checkpoint_path |
| global_step = int(ckpt_path.split('/')[-1].split('-')[-1]) |
| assert global_step >= min_iter, 'training stage not reached' |
|
|
| start_iter = global_step + 1 |
| if start_iter > max_iter: |
| print('-- train: max_iter reached') |
| return |
| else: |
| start_iter = min_iter + 1 |
|
|
| print('-- training from i = {} to {}'.format(start_iter, max_iter)) |
|
|
| assert (max_iter - start_iter + 1) % save_interval == 0 |
| for i in range(start_iter, max_iter + 1, save_interval): |
| self.train(i, i + save_interval - 1, i - (min_iter + 1)) |
| self.eval(1) |
|
|
| if self.plot_proc: |
| self.plot_proc.join() |
|
|
| def get_train_and_loss_ops(self, batch, learning_rate, global_step): |
| if self.params['flownet'] == 'resnet': |
| opt = tf.train.MomentumOptimizer(learning_rate, 0.9) |
| else: |
| opt = tf.train.AdamOptimizer(beta1=0.9, beta2=0.999, |
| learning_rate=learning_rate) |
| def _add_summaries(): |
| _add_loss_summaries() |
| _add_param_summaries() |
| if self.debug: |
| _add_image_summaries() |
|
|
| if len(self.devices) == 1: |
| loss_ = self.loss_fn(batch, self.params, self.normalization) |
| train_op = opt.minimize(loss_) |
| _add_summaries() |
| else: |
| tower_grads = [] |
| with tf.variable_scope(tf.get_variable_scope()): |
| for i, devid in enumerate(self.devices): |
| with tf.device(devid): |
| with tf.name_scope('tower_{}'.format(i)) as scope: |
| loss_ = self.loss_fn(batch, self.params, self.normalization) |
| _add_summaries() |
|
|
| |
| tf.get_variable_scope().reuse_variables() |
|
|
| |
| tower_summaries = tf.get_collection(tf.GraphKeys.SUMMARIES, |
| scope) |
| grads = opt.compute_gradients(loss_) |
| tower_grads.append(grads) |
|
|
| grads = average_gradients(tower_grads) |
| apply_gradient_op = opt.apply_gradients(grads) |
| train_op = apply_gradient_op |
|
|
| return train_op, loss_ |
|
|
| def train(self, start_iter, max_iter, iter_offset): |
| ckpt = tf.train.get_checkpoint_state(self.ckpt_dir) |
|
|
| with tf.Graph().as_default(), tf.device(self.shared_device): |
| batch = self.train_batch_fn(iter_offset) |
|
|
| with tf.name_scope('params') as scope: |
| learning_rate_ = util.summarized_placeholder('learning_rate', 'train') |
| summaries = tf.get_collection(tf.GraphKeys.SUMMARIES, scope) |
|
|
| global_step_ = tf.placeholder(tf.int32, name="global_step") |
|
|
| train_op, loss_ = self.get_train_and_loss_ops(batch, learning_rate_, global_step_) |
|
|
| summaries = tf.get_collection(tf.GraphKeys.SUMMARIES) |
| summary_ = tf.summary.merge(summaries) |
|
|
| sess_config = tf.ConfigProto(allow_soft_placement=True) |
|
|
| with tf.Session(config=sess_config) as sess: |
| if self.debug: |
| summary_writer = tf.summary.FileWriter(self.train_summaries_dir, |
| sess.graph) |
| run_options = tf.RunOptions(trace_level=tf.RunOptions.FULL_TRACE) |
| run_metadata = tf.RunMetadata() |
| else: |
| summary_writer = tf.summary.FileWriter(self.train_summaries_dir) |
| run_options = None |
| run_metadata = None |
|
|
| saver = restore_networks(sess, self.params, ckpt) |
|
|
| coord = tf.train.Coordinator() |
| threads = tf.train.start_queue_runners(sess=sess, coord=coord) |
|
|
| for local_i, i in enumerate(range(start_iter, max_iter + 1)): |
| |
| |
| decay_iters = local_i + iter_offset |
| if 'manual_decay_lrs' in self.params \ |
| and 'manual_decay_iters' in self.params: |
| decay_index = 0 |
| iter_counter = 0 |
| for decay_i, manual_decay_iter in enumerate(self.params['manual_decay_iters']): |
| iter_counter += manual_decay_iter |
| if decay_iters <= iter_counter: |
| decay_index = decay_i |
| break |
| learning_rate = self.params['manual_decay_lrs'][decay_index] |
| else: |
| decay_interval = self.params['decay_interval'] |
| decay_after = self.params.get('decay_after', 0) |
| if decay_iters >= decay_after: |
| decay_minimum = decay_after / decay_interval |
| decay = (decay_iters // decay_interval) - decay_minimum |
| learning_rate = self.params['learning_rate'] / (2 ** decay) |
| else: |
| learning_rate = self.params['learning_rate'] |
|
|
| feed_dict = {learning_rate_: learning_rate, global_step_: i} |
| _, loss = sess.run( |
| [train_op, loss_], |
| feed_dict=feed_dict, |
| options=run_options, |
| run_metadata=run_metadata) |
|
|
| if i == 1 or i % self.params['display_interval'] == 0: |
| summary = sess.run(summary_, feed_dict=feed_dict) |
| summary_writer.add_summary(summary, i) |
| print("-- train: i = {}, loss = {}".format(i, loss)) |
|
|
| save_path = os.path.join(self.ckpt_dir, 'model.ckpt') |
| saver.save(sess, save_path, global_step=max_iter) |
|
|
| summary_writer.close() |
| coord.request_stop() |
| coord.join(threads) |
|
|
| def eval(self, num): |
| assert num == 1 |
|
|
| with tf.Graph().as_default(): |
| inputs = self.eval_batch_fn() |
| im1, im2, input_shape = inputs[:3] |
| truths = inputs[3:] |
|
|
| height, width, _ = tf.unstack(tf.squeeze(input_shape), num=3, axis=0) |
| im1 = resize_input(im1, height, width, 384, 1280) |
| im2 = resize_input(im2, height, width, 384, 1280) |
|
|
| _, flow, flow_bw = unsupervised_loss( |
| (im1, im2), |
| params=self.params, |
| normalization=self.normalization, |
| augment=False, return_flow=True) |
|
|
| im1 = resize_output(im1, height, width, 3) |
| im2 = resize_output(im2, height, width, 3) |
| flow = resize_output_flow(flow, height, width, 2) |
| flow_bw = resize_output_flow(flow_bw, height, width, 2) |
|
|
| variables_to_restore = tf.all_variables() |
|
|
| images_ = [image_warp(im1, flow) / 255, |
| flow_to_color(flow), |
| 1 - (1 - occlusion(flow, flow_bw)[0]) * create_outgoing_mask(flow) , |
| forward_warp(flow_bw) < DISOCC_THRESH] |
| image_names = ['warped image', 'flow', 'occ', 'reverse disocc'] |
|
|
| values_ = [] |
| averages_ = [] |
| truth_tuples = [] |
| if len(truths) == 4: |
| flow_occ, mask_occ, flow_noc, mask_noc = truths |
| flow_occ = resize_output_crop(flow_occ, height, width, 2) |
| flow_noc = resize_output_crop(flow_noc, height, width, 2) |
| mask_occ = resize_output_crop(mask_occ, height, width, 1) |
| mask_noc = resize_output_crop(mask_noc, height, width, 1) |
|
|
| truth_tuples.append(('occluded', flow_occ, mask_occ)) |
| truth_tuples.append(('non-occluded', flow_noc, mask_noc)) |
| images_ += [flow_error_image(flow, flow_occ, mask_occ, mask_noc)] |
| image_names += ['flow error'] |
| else: |
| raise NotImplementedError() |
| truth_tuples.append(('flow', truths[0], truths[1])) |
|
|
| for name, gt_flow, mask in truth_tuples: |
| error_ = flow_error_avg(gt_flow, flow, mask) |
| error_avg_ = summarized_placeholder('AEE/' + name, key='eval_avg') |
| outliers_ = outlier_pct(gt_flow, flow, mask) |
| outliers_avg = summarized_placeholder('outliers/' + name, |
| key='eval_avg') |
| values_.extend([error_, outliers_]) |
| averages_.extend([error_avg_, outliers_avg]) |
|
|
| losses = tf.get_collection('losses') |
| for l in losses: |
| values_.append(l) |
| tensor_name = re.sub('tower_[0-9]*/', '', l.op.name) |
| loss_avg_ = summarized_placeholder(tensor_name, key='eval_avg') |
| averages_.append(loss_avg_) |
|
|
| ckpt = tf.train.get_checkpoint_state(self.ckpt_dir) |
| assert ckpt is not None, "No checkpoints to evaluate" |
|
|
| |
| |
| ckpt_path = ckpt.model_checkpoint_path |
|
|
| with tf.Session() as sess: |
| summary_writer = tf.summary.FileWriter(self.eval_summaries_dir) |
| saver = tf.train.Saver(variables_to_restore) |
|
|
| sess.run(tf.global_variables_initializer()) |
| sess.run(tf.local_variables_initializer()) |
|
|
| restore_networks(sess, self.params, ckpt) |
| global_step = ckpt_path.split('/')[-1].split('-')[-1] |
|
|
| coord = tf.train.Coordinator() |
| threads = tf.train.start_queue_runners(sess=sess, |
| coord=coord) |
| averages = np.zeros(len(averages_)) |
| num_iters = 0 |
|
|
| image_lists = [] |
| try: |
| while not coord.should_stop(): |
| results = sess.run(values_ + images_) |
| values = results[:len(averages_)] |
| images = results[len(averages_):] |
| image_lists.append(images) |
| averages += values |
| num_iters += 1 |
| except tf.errors.OutOfRangeError: |
| pass |
|
|
| averages /= num_iters |
| feed = {k: v for (k, v) in zip(averages_, averages)} |
|
|
| summary_ = tf.summary.merge_all('eval_avg') |
| summary = sess.run(summary_, feed_dict=feed) |
| summary_writer.add_summary(summary, global_step) |
|
|
| print("-- eval: i = {}".format(global_step)) |
|
|
| coord.request_stop() |
| coord.join(threads) |
| summary_writer.close() |
|
|
| if self.interactive_plot: |
| if self.plot_proc: |
| self.plot_proc.terminate() |
| self.plot_proc = Process(target=_eval_plot, |
| args=([image_lists], image_names, |
| "{} (i={})".format(self.experiment, |
| global_step))) |
| self.plot_proc.start() |
|
|
|
|
| def average_gradients(tower_grads): |
| """Calculate the average gradient for each shared variable across all towers. |
| Note that this function provides a synchronization point across all towers. |
| Args: |
| tower_grads: List of lists of (gradient, variable) tuples. The outer list |
| is over individual gradients. The inner list is over the gradient |
| calculation for each tower. |
| Returns: |
| List of pairs of (gradient, variable) where the gradient has been averaged |
| across all towers. |
| """ |
| average_grads = [] |
| for grad_and_vars in zip(*tower_grads): |
| |
| |
| grads = [] |
| for g, _ in grad_and_vars: |
| if g is not None: |
| |
| expanded_g = tf.expand_dims(g, 0) |
|
|
| |
| grads.append(expanded_g) |
| if grads != []: |
| |
| grad = tf.concat(grads, 0) |
| grad = tf.reduce_mean(grad, 0) |
|
|
| |
| |
| |
| v = grad_and_vars[0][1] |
| grad_and_var = (grad, v) |
| average_grads.append(grad_and_var) |
| return average_grads |
|
|