Spaces:
Running
Running
# Copyright 2016 The TensorFlow Authors All Rights Reserved. | |
# | |
# Licensed under the Apache License, Version 2.0 (the "License"); | |
# you may not use this file except in compliance with the License. | |
# You may obtain a copy of the License at | |
# | |
# http://www.apache.org/licenses/LICENSE-2.0 | |
# | |
# Unless required by applicable law or agreed to in writing, software | |
# distributed under the License is distributed on an "AS IS" BASIS, | |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
# See the License for the specific language governing permissions and | |
# limitations under the License. | |
# ============================================================================== | |
"""Various losses for training navigation agents. | |
Defines various loss functions for navigation agents, | |
compute_losses_multi_or. | |
""" | |
import os, numpy as np | |
import matplotlib.pyplot as plt | |
import tensorflow as tf | |
from tensorflow.contrib import slim | |
from tensorflow.contrib.slim import arg_scope | |
from tensorflow.contrib.slim.nets import resnet_v2 | |
from tensorflow.python.training import moving_averages | |
import logging | |
from src import utils | |
import src.file_utils as fu | |
from tfcode import tf_utils | |
def compute_losses_multi_or(logits, actions_one_hot, weights=None, | |
num_actions=-1, data_loss_wt=1., reg_loss_wt=1., | |
ewma_decay=0.99, reg_loss_op=None): | |
assert(num_actions > 0), 'num_actions must be specified and must be > 0.' | |
with tf.name_scope('loss'): | |
if weights is None: | |
weight = tf.ones_like(actions_one_hot, dtype=tf.float32, name='weight') | |
actions_one_hot = tf.cast(tf.reshape(actions_one_hot, [-1, num_actions], | |
're_actions_one_hot'), tf.float32) | |
weights = tf.reduce_sum(tf.reshape(weights, [-1, num_actions], 're_weight'), | |
reduction_indices=1) | |
total = tf.reduce_sum(weights) | |
action_prob = tf.nn.softmax(logits) | |
action_prob = tf.reduce_sum(tf.multiply(action_prob, actions_one_hot), | |
reduction_indices=1) | |
example_loss = -tf.log(tf.maximum(tf.constant(1e-4), action_prob)) | |
data_loss_op = tf.reduce_sum(example_loss * weights) / total | |
if reg_loss_op is None: | |
if reg_loss_wt > 0: | |
reg_loss_op = tf.add_n(tf.losses.get_regularization_losses()) | |
else: | |
reg_loss_op = tf.constant(0.) | |
if reg_loss_wt > 0: | |
total_loss_op = data_loss_wt*data_loss_op + reg_loss_wt*reg_loss_op | |
else: | |
total_loss_op = data_loss_wt*data_loss_op | |
is_correct = tf.cast(tf.greater(action_prob, 0.5, name='pred_class'), tf.float32) | |
acc_op = tf.reduce_sum(is_correct*weights) / total | |
ewma_acc_op = moving_averages.weighted_moving_average( | |
acc_op, ewma_decay, weight=total, name='ewma_acc') | |
acc_ops = [ewma_acc_op] | |
return reg_loss_op, data_loss_op, total_loss_op, acc_ops | |
def get_repr_from_image(images_reshaped, modalities, data_augment, encoder, | |
freeze_conv, wt_decay, is_training): | |
# Pass image through lots of convolutional layers, to obtain pool5 | |
if modalities == ['rgb']: | |
with tf.name_scope('pre_rgb'): | |
x = (images_reshaped + 128.) / 255. # Convert to brightness between 0 and 1. | |
if data_augment.relight and is_training: | |
x = tf_utils.distort_image(x, fast_mode=data_augment.relight_fast) | |
x = (x-0.5)*2.0 | |
scope_name = encoder | |
elif modalities == ['depth']: | |
with tf.name_scope('pre_d'): | |
d_image = images_reshaped | |
x = 2*(d_image[...,0] - 80.0)/100.0 | |
y = d_image[...,1] | |
d_image = tf.concat([tf.expand_dims(x, -1), tf.expand_dims(y, -1)], 3) | |
x = d_image | |
scope_name = 'd_'+encoder | |
resnet_is_training = is_training and (not freeze_conv) | |
with slim.arg_scope(resnet_v2.resnet_utils.resnet_arg_scope(resnet_is_training)): | |
fn = getattr(tf_utils, encoder) | |
x, end_points = fn(x, num_classes=None, global_pool=False, | |
output_stride=None, reuse=None, | |
scope=scope_name) | |
vars_ = slim.get_variables_to_restore() | |
conv_feat = x | |
return conv_feat, vars_ | |
def default_train_step_kwargs(m, obj, logdir, rng_seed, is_chief, num_steps, | |
iters, train_display_interval, | |
dagger_sample_bn_false): | |
train_step_kwargs = {} | |
train_step_kwargs['obj'] = obj | |
train_step_kwargs['m'] = m | |
# rng_data has 2 independent rngs, one for sampling episodes and one for | |
# sampling perturbs (so that we can make results reproducible. | |
train_step_kwargs['rng_data'] = [np.random.RandomState(rng_seed), | |
np.random.RandomState(rng_seed)] | |
train_step_kwargs['rng_action'] = np.random.RandomState(rng_seed) | |
if is_chief: | |
train_step_kwargs['writer'] = tf.summary.FileWriter(logdir) #, m.tf_graph) | |
else: | |
train_step_kwargs['writer'] = None | |
train_step_kwargs['iters'] = iters | |
train_step_kwargs['train_display_interval'] = train_display_interval | |
train_step_kwargs['num_steps'] = num_steps | |
train_step_kwargs['logdir'] = logdir | |
train_step_kwargs['dagger_sample_bn_false'] = dagger_sample_bn_false | |
return train_step_kwargs | |
# Utilities for visualizing and analysing validation output. | |
def save_d_at_t(outputs, global_step, output_dir, metric_summary, N): | |
"""Save distance to goal at all time steps. | |
Args: | |
outputs : [gt_dist_to_goal]. | |
global_step : number of iterations. | |
output_dir : output directory. | |
metric_summary : to append scalars to summary. | |
N : number of outputs to process. | |
""" | |
d_at_t = np.concatenate(map(lambda x: x[0][:,:,0]*1, outputs), axis=0) | |
fig, axes = utils.subplot(plt, (1,1), (5,5)) | |
axes.plot(np.arange(d_at_t.shape[1]), np.mean(d_at_t, axis=0), 'r.') | |
axes.set_xlabel('time step') | |
axes.set_ylabel('dist to next goal') | |
axes.grid('on') | |
file_name = os.path.join(output_dir, 'dist_at_t_{:d}.png'.format(global_step)) | |
with fu.fopen(file_name, 'w') as f: | |
fig.savefig(f, bbox_inches='tight', transparent=True, pad_inches=0) | |
file_name = os.path.join(output_dir, 'dist_at_t_{:d}.pkl'.format(global_step)) | |
utils.save_variables(file_name, [d_at_t], ['d_at_t'], overwrite=True) | |
plt.close(fig) | |
return None | |
def save_all(outputs, global_step, output_dir, metric_summary, N): | |
"""Save numerous statistics. | |
Args: | |
outputs : [locs, goal_loc, gt_dist_to_goal, node_ids, perturbs] | |
global_step : number of iterations. | |
output_dir : output directory. | |
metric_summary : to append scalars to summary. | |
N : number of outputs to process. | |
""" | |
all_locs = np.concatenate(map(lambda x: x[0], outputs), axis=0) | |
all_goal_locs = np.concatenate(map(lambda x: x[1], outputs), axis=0) | |
all_d_at_t = np.concatenate(map(lambda x: x[2][:,:,0]*1, outputs), axis=0) | |
all_node_ids = np.concatenate(map(lambda x: x[3], outputs), axis=0) | |
all_perturbs = np.concatenate(map(lambda x: x[4], outputs), axis=0) | |
file_name = os.path.join(output_dir, 'all_locs_at_t_{:d}.pkl'.format(global_step)) | |
vars = [all_locs, all_goal_locs, all_d_at_t, all_node_ids, all_perturbs] | |
var_names = ['all_locs', 'all_goal_locs', 'all_d_at_t', 'all_node_ids', 'all_perturbs'] | |
utils.save_variables(file_name, vars, var_names, overwrite=True) | |
return None | |
def eval_ap(outputs, global_step, output_dir, metric_summary, N, num_classes=4): | |
"""Processes the collected outputs to compute AP for action prediction. | |
Args: | |
outputs : [logits, labels] | |
global_step : global_step. | |
output_dir : where to store results. | |
metric_summary : summary object to add summaries to. | |
N : number of outputs to process. | |
num_classes : number of classes to compute AP over, and to reshape tensors. | |
""" | |
if N >= 0: | |
outputs = outputs[:N] | |
logits = np.concatenate(map(lambda x: x[0], outputs), axis=0).reshape((-1, num_classes)) | |
labels = np.concatenate(map(lambda x: x[1], outputs), axis=0).reshape((-1, num_classes)) | |
aps = [] | |
for i in range(logits.shape[1]): | |
ap, rec, prec = utils.calc_pr(labels[:,i], logits[:,i]) | |
ap = ap[0] | |
tf_utils.add_value_to_summary(metric_summary, 'aps/ap_{:d}: '.format(i), ap) | |
aps.append(ap) | |
return aps | |
def eval_dist(outputs, global_step, output_dir, metric_summary, N): | |
"""Processes the collected outputs during validation to | |
1. Plot the distance over time curve. | |
2. Compute mean and median distances. | |
3. Plots histogram of end distances. | |
Args: | |
outputs : [locs, goal_loc, gt_dist_to_goal]. | |
global_step : global_step. | |
output_dir : where to store results. | |
metric_summary : summary object to add summaries to. | |
N : number of outputs to process. | |
""" | |
SUCCESS_THRESH = 3 | |
if N >= 0: | |
outputs = outputs[:N] | |
# Plot distance at time t. | |
d_at_t = [] | |
for i in range(len(outputs)): | |
locs, goal_loc, gt_dist_to_goal = outputs[i] | |
d_at_t.append(gt_dist_to_goal[:,:,0]*1) | |
# Plot the distance. | |
fig, axes = utils.subplot(plt, (1,1), (5,5)) | |
d_at_t = np.concatenate(d_at_t, axis=0) | |
axes.plot(np.arange(d_at_t.shape[1]), np.mean(d_at_t, axis=0), 'r.') | |
axes.set_xlabel('time step') | |
axes.set_ylabel('dist to next goal') | |
axes.grid('on') | |
file_name = os.path.join(output_dir, 'dist_at_t_{:d}.png'.format(global_step)) | |
with fu.fopen(file_name, 'w') as f: | |
fig.savefig(f, bbox_inches='tight', transparent=True, pad_inches=0) | |
file_name = os.path.join(output_dir, 'dist_at_t_{:d}.pkl'.format(global_step)) | |
utils.save_variables(file_name, [d_at_t], ['d_at_t'], overwrite=True) | |
plt.close(fig) | |
# Plot the trajectories and the init_distance and final distance. | |
d_inits = [] | |
d_ends = [] | |
for i in range(len(outputs)): | |
locs, goal_loc, gt_dist_to_goal = outputs[i] | |
d_inits.append(gt_dist_to_goal[:,0,0]*1) | |
d_ends.append(gt_dist_to_goal[:,-1,0]*1) | |
# Plot the distance. | |
fig, axes = utils.subplot(plt, (1,1), (5,5)) | |
d_inits = np.concatenate(d_inits, axis=0) | |
d_ends = np.concatenate(d_ends, axis=0) | |
axes.plot(d_inits+np.random.rand(*(d_inits.shape))-0.5, | |
d_ends+np.random.rand(*(d_ends.shape))-0.5, '.', mec='red', mew=1.0) | |
axes.set_xlabel('init dist'); axes.set_ylabel('final dist'); | |
axes.grid('on'); axes.axis('equal'); | |
title_str = 'mean: {:0.1f}, 50: {:0.1f}, 75: {:0.2f}, s: {:0.1f}' | |
title_str = title_str.format( | |
np.mean(d_ends), np.median(d_ends), np.percentile(d_ends, q=75), | |
100*(np.mean(d_ends <= SUCCESS_THRESH))) | |
axes.set_title(title_str) | |
file_name = os.path.join(output_dir, 'dist_{:d}.png'.format(global_step)) | |
with fu.fopen(file_name, 'w') as f: | |
fig.savefig(f, bbox_inches='tight', transparent=True, pad_inches=0) | |
file_name = os.path.join(output_dir, 'dist_{:d}.pkl'.format(global_step)) | |
utils.save_variables(file_name, [d_inits, d_ends], ['d_inits', 'd_ends'], | |
overwrite=True) | |
plt.close(fig) | |
# Plot the histogram of the end_distance. | |
with plt.style.context('seaborn-white'): | |
d_ends_ = np.sort(d_ends) | |
d_inits_ = np.sort(d_inits) | |
leg = []; | |
fig, ax = utils.subplot(plt, (1,1), (5,5)) | |
ax.grid('on') | |
ax.set_xlabel('Distance from goal'); ax.xaxis.label.set_fontsize(16); | |
ax.set_ylabel('Fraction of data'); ax.yaxis.label.set_fontsize(16); | |
ax.plot(d_ends_, np.arange(d_ends_.size)*1./d_ends_.size, 'r') | |
ax.plot(d_inits_, np.arange(d_inits_.size)*1./d_inits_.size, 'k') | |
leg.append('Final'); leg.append('Init'); | |
ax.legend(leg, fontsize='x-large'); | |
ax.set_axis_on() | |
title_str = 'mean: {:0.1f}, 50: {:0.1f}, 75: {:0.2f}, s: {:0.1f}' | |
title_str = title_str.format( | |
np.mean(d_ends), np.median(d_ends), np.percentile(d_ends, q=75), | |
100*(np.mean(d_ends <= SUCCESS_THRESH))) | |
ax.set_title(title_str) | |
file_name = os.path.join(output_dir, 'dist_hist_{:d}.png'.format(global_step)) | |
with fu.fopen(file_name, 'w') as f: | |
fig.savefig(f, bbox_inches='tight', transparent=True, pad_inches=0) | |
# Log distance metrics. | |
tf_utils.add_value_to_summary(metric_summary, 'dists/success_init: ', | |
100*(np.mean(d_inits <= SUCCESS_THRESH))) | |
tf_utils.add_value_to_summary(metric_summary, 'dists/success_end: ', | |
100*(np.mean(d_ends <= SUCCESS_THRESH))) | |
tf_utils.add_value_to_summary(metric_summary, 'dists/dist_init (75): ', | |
np.percentile(d_inits, q=75)) | |
tf_utils.add_value_to_summary(metric_summary, 'dists/dist_end (75): ', | |
np.percentile(d_ends, q=75)) | |
tf_utils.add_value_to_summary(metric_summary, 'dists/dist_init (median): ', | |
np.median(d_inits)) | |
tf_utils.add_value_to_summary(metric_summary, 'dists/dist_end (median): ', | |
np.median(d_ends)) | |
tf_utils.add_value_to_summary(metric_summary, 'dists/dist_init (mean): ', | |
np.mean(d_inits)) | |
tf_utils.add_value_to_summary(metric_summary, 'dists/dist_end (mean): ', | |
np.mean(d_ends)) | |
return np.median(d_inits), np.median(d_ends), np.mean(d_inits), np.mean(d_ends), \ | |
np.percentile(d_inits, q=75), np.percentile(d_ends, q=75), \ | |
100*(np.mean(d_inits) <= SUCCESS_THRESH), 100*(np.mean(d_ends) <= SUCCESS_THRESH) | |
def plot_trajectories(outputs, global_step, output_dir, metric_summary, N): | |
"""Processes the collected outputs during validation to plot the trajectories | |
in the top view. | |
Args: | |
outputs : [locs, orig_maps, goal_loc]. | |
global_step : global_step. | |
output_dir : where to store results. | |
metric_summary : summary object to add summaries to. | |
N : number of outputs to process. | |
""" | |
if N >= 0: | |
outputs = outputs[:N] | |
N = len(outputs) | |
plt.set_cmap('gray') | |
fig, axes = utils.subplot(plt, (N, outputs[0][1].shape[0]), (5,5)) | |
axes = axes.ravel()[::-1].tolist() | |
for i in range(N): | |
locs, orig_maps, goal_loc = outputs[i] | |
is_semantic = np.isnan(goal_loc[0,0,1]) | |
for j in range(orig_maps.shape[0]): | |
ax = axes.pop(); | |
ax.plot(locs[j,0,0], locs[j,0,1], 'ys') | |
# Plot one by one, so that they come in different colors. | |
for k in range(goal_loc.shape[1]): | |
if not is_semantic: | |
ax.plot(goal_loc[j,k,0], goal_loc[j,k,1], 's') | |
if False: | |
ax.plot(locs[j,:,0], locs[j,:,1], 'r.', ms=3) | |
ax.imshow(orig_maps[j,0,:,:,0], origin='lower') | |
ax.set_axis_off(); | |
else: | |
ax.scatter(locs[j,:,0], locs[j,:,1], c=np.arange(locs.shape[1]), | |
cmap='jet', s=10, lw=0) | |
ax.imshow(orig_maps[j,0,:,:,0], origin='lower', vmin=-1.0, vmax=2.0) | |
if not is_semantic: | |
xymin = np.minimum(np.min(goal_loc[j,:,:], axis=0), np.min(locs[j,:,:], axis=0)) | |
xymax = np.maximum(np.max(goal_loc[j,:,:], axis=0), np.max(locs[j,:,:], axis=0)) | |
else: | |
xymin = np.min(locs[j,:,:], axis=0) | |
xymax = np.max(locs[j,:,:], axis=0) | |
xy1 = (xymax+xymin)/2. - np.maximum(np.max(xymax-xymin), 12) | |
xy2 = (xymax+xymin)/2. + np.maximum(np.max(xymax-xymin), 12) | |
ax.set_xlim([xy1[0], xy2[0]]) | |
ax.set_ylim([xy1[1], xy2[1]]) | |
ax.set_axis_off() | |
file_name = os.path.join(output_dir, 'trajectory_{:d}.png'.format(global_step)) | |
with fu.fopen(file_name, 'w') as f: | |
fig.savefig(f, bbox_inches='tight', transparent=True, pad_inches=0) | |
plt.close(fig) | |
return None | |
def add_default_summaries(mode, arop_full_summary_iters, summarize_ops, | |
summarize_names, to_aggregate, action_prob_op, | |
input_tensors, scope_name): | |
assert(mode == 'train' or mode == 'val' or mode == 'test'), \ | |
'add_default_summaries mode is neither train or val or test.' | |
s_ops = tf_utils.get_default_summary_ops() | |
if mode == 'train': | |
s_ops.summary_ops, s_ops.print_summary_ops, additional_return_ops, \ | |
arop_summary_iters, arop_eval_fns = tf_utils.simple_summaries( | |
summarize_ops, summarize_names, mode, to_aggregate=False, | |
scope_name=scope_name) | |
s_ops.additional_return_ops += additional_return_ops | |
s_ops.arop_summary_iters += arop_summary_iters | |
s_ops.arop_eval_fns += arop_eval_fns | |
elif mode == 'val': | |
s_ops.summary_ops, s_ops.print_summary_ops, additional_return_ops, \ | |
arop_summary_iters, arop_eval_fns = tf_utils.simple_summaries( | |
summarize_ops, summarize_names, mode, to_aggregate=to_aggregate, | |
scope_name=scope_name) | |
s_ops.additional_return_ops += additional_return_ops | |
s_ops.arop_summary_iters += arop_summary_iters | |
s_ops.arop_eval_fns += arop_eval_fns | |
elif mode == 'test': | |
s_ops.summary_ops, s_ops.print_summary_ops, additional_return_ops, \ | |
arop_summary_iters, arop_eval_fns = tf_utils.simple_summaries( | |
[], [], mode, to_aggregate=[], scope_name=scope_name) | |
s_ops.additional_return_ops += additional_return_ops | |
s_ops.arop_summary_iters += arop_summary_iters | |
s_ops.arop_eval_fns += arop_eval_fns | |
if mode == 'val': | |
arop = s_ops.additional_return_ops | |
arop += [[action_prob_op, input_tensors['train']['action']]] | |
arop += [[input_tensors['step']['loc_on_map'], | |
input_tensors['common']['goal_loc'], | |
input_tensors['step']['gt_dist_to_goal']]] | |
arop += [[input_tensors['step']['loc_on_map'], | |
input_tensors['common']['orig_maps'], | |
input_tensors['common']['goal_loc']]] | |
s_ops.arop_summary_iters += [-1, arop_full_summary_iters, | |
arop_full_summary_iters] | |
s_ops.arop_eval_fns += [eval_ap, eval_dist, plot_trajectories] | |
elif mode == 'test': | |
arop = s_ops.additional_return_ops | |
arop += [[input_tensors['step']['loc_on_map'], | |
input_tensors['common']['goal_loc'], | |
input_tensors['step']['gt_dist_to_goal']]] | |
arop += [[input_tensors['step']['gt_dist_to_goal']]] | |
arop += [[input_tensors['step']['loc_on_map'], | |
input_tensors['common']['goal_loc'], | |
input_tensors['step']['gt_dist_to_goal'], | |
input_tensors['step']['node_ids'], | |
input_tensors['step']['perturbs']]] | |
arop += [[input_tensors['step']['loc_on_map'], | |
input_tensors['common']['orig_maps'], | |
input_tensors['common']['goal_loc']]] | |
s_ops.arop_summary_iters += [-1, -1, -1, arop_full_summary_iters] | |
s_ops.arop_eval_fns += [eval_dist, save_d_at_t, save_all, | |
plot_trajectories] | |
return s_ops | |