Spaces:
Running
Running
# Copyright 2018 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. | |
# ============================================================================== | |
"""Utility functions for training.""" | |
import collections | |
import six | |
import tensorflow as tf | |
from deeplab.core import preprocess_utils | |
from deeplab.utils import train_utils | |
from feelvos.utils import embedding_utils | |
from feelvos.utils import eval_utils | |
slim = tf.contrib.slim | |
add_softmax_cross_entropy_loss_for_each_scale = ( | |
train_utils.add_softmax_cross_entropy_loss_for_each_scale) | |
get_model_gradient_multipliers = train_utils.get_model_gradient_multipliers | |
get_model_learning_rate = train_utils.get_model_learning_rate | |
resolve_shape = preprocess_utils.resolve_shape | |
def add_triplet_loss_for_each_scale(batch_size, num_frames_per_video, | |
embedding_dim, scales_to_embeddings, | |
labels, scope): | |
"""Adds triplet loss for logits of each scale. | |
Args: | |
batch_size: Int, the number of video chunks sampled per batch | |
num_frames_per_video: Int, the number of frames per video. | |
embedding_dim: Int, the dimension of the learned embedding | |
scales_to_embeddings: A map from embedding names for different scales to | |
embeddings. The embeddings have shape [batch, embeddings_height, | |
embeddings_width, embedding_dim]. | |
labels: Groundtruth labels with shape [batch, image_height, image_width, 1]. | |
scope: String, the scope for the loss. | |
Raises: | |
ValueError: labels is None. | |
""" | |
if labels is None: | |
raise ValueError('No label for triplet loss.') | |
for scale, embeddings in scales_to_embeddings.iteritems(): | |
loss_scope = None | |
if scope: | |
loss_scope = '%s_%s' % (scope, scale) | |
# Label is downsampled to the same size as logits. | |
scaled_labels = tf.image.resize_nearest_neighbor( | |
labels, | |
resolve_shape(embeddings, 4)[1:3], | |
align_corners=True) | |
# Reshape from [batch * num_frames, ...] to [batch, num_frames, ...]. | |
h = tf.shape(embeddings)[1] | |
w = tf.shape(embeddings)[2] | |
new_labels_shape = tf.stack([batch_size, num_frames_per_video, h, w, 1]) | |
reshaped_labels = tf.reshape(scaled_labels, new_labels_shape) | |
new_embeddings_shape = tf.stack([batch_size, num_frames_per_video, h, w, | |
-1]) | |
reshaped_embeddings = tf.reshape(embeddings, new_embeddings_shape) | |
with tf.name_scope(loss_scope): | |
total_loss = tf.constant(0, dtype=tf.float32) | |
for n in range(batch_size): | |
embedding = reshaped_embeddings[n] | |
label = reshaped_labels[n] | |
n_pixels = h * w | |
n_anchors_used = 256 | |
sampled_anchor_indices = tf.random_shuffle(tf.range(n_pixels))[ | |
:n_anchors_used] | |
anchors_pool = tf.reshape(embedding[0], [-1, embedding_dim]) | |
anchors_pool_classes = tf.reshape(label[0], [-1]) | |
anchors = tf.gather(anchors_pool, sampled_anchor_indices) | |
anchor_classes = tf.gather(anchors_pool_classes, sampled_anchor_indices) | |
pos_neg_pool = tf.reshape(embedding[1:], [-1, embedding_dim]) | |
pos_neg_pool_classes = tf.reshape(label[1:], [-1]) | |
dists = embedding_utils.pairwise_distances(anchors, pos_neg_pool) | |
pos_mask = tf.equal(anchor_classes[:, tf.newaxis], | |
pos_neg_pool_classes[tf.newaxis, :]) | |
neg_mask = tf.logical_not(pos_mask) | |
pos_mask_f = tf.cast(pos_mask, tf.float32) | |
neg_mask_f = tf.cast(neg_mask, tf.float32) | |
pos_dists = pos_mask_f * dists + 1e20 * neg_mask_f | |
neg_dists = neg_mask_f * dists + 1e20 * pos_mask_f | |
pos_dists_min = tf.reduce_min(pos_dists, axis=1) | |
neg_dists_min = tf.reduce_min(neg_dists, axis=1) | |
margin = 1.0 | |
loss = tf.nn.relu(pos_dists_min - neg_dists_min + margin) | |
# Handle case that no positive is present (per anchor). | |
any_pos = tf.reduce_any(pos_mask, axis=1) | |
loss *= tf.cast(any_pos, tf.float32) | |
# Average over anchors | |
loss = tf.reduce_mean(loss, axis=0) | |
total_loss += loss | |
total_loss /= batch_size | |
# Scale the loss up a bit. | |
total_loss *= 3.0 | |
tf.add_to_collection(tf.GraphKeys.LOSSES, total_loss) | |
def add_dynamic_softmax_cross_entropy_loss_for_each_scale( | |
scales_to_logits, labels, ignore_label, loss_weight=1.0, | |
upsample_logits=True, scope=None, top_k_percent_pixels=1.0, | |
hard_example_mining_step=100000): | |
"""Adds softmax cross entropy loss per scale for logits with varying classes. | |
Also adds summaries for mIoU. | |
Args: | |
scales_to_logits: A map from logits names for different scales to logits. | |
The logits are a list of length batch_size of tensors of shape | |
[time, logits_height, logits_width, num_classes]. | |
labels: Groundtruth labels with shape [batch_size * time, image_height, | |
image_width, 1]. | |
ignore_label: Integer, label to ignore. | |
loss_weight: Float, loss weight. | |
upsample_logits: Boolean, upsample logits or not. | |
scope: String, the scope for the loss. | |
top_k_percent_pixels: A float, the value lies in [0.0, 1.0]. When its | |
value < 1.0, only compute the loss for the top k percent pixels (e.g., | |
the top 20% pixels). This is useful for hard pixel mining. | |
hard_example_mining_step: An integer, the training step in which the | |
hard exampling mining kicks off. Note that we gradually reduce the | |
mining percent to the top_k_percent_pixels. For example, if | |
hard_example_mining_step=100K and top_k_percent_pixels=0.25, then | |
mining percent will gradually reduce from 100% to 25% until 100K steps | |
after which we only mine top 25% pixels. | |
Raises: | |
ValueError: Label or logits is None. | |
""" | |
if labels is None: | |
raise ValueError('No label for softmax cross entropy loss.') | |
if top_k_percent_pixels < 0 or top_k_percent_pixels > 1: | |
raise ValueError('Unexpected value of top_k_percent_pixels.') | |
for scale, logits in six.iteritems(scales_to_logits): | |
loss_scope = None | |
if scope: | |
loss_scope = '%s_%s' % (scope, scale) | |
if upsample_logits: | |
# Label is not downsampled, and instead we upsample logits. | |
assert isinstance(logits, collections.Sequence) | |
logits = [tf.image.resize_bilinear( | |
x, | |
preprocess_utils.resolve_shape(labels, 4)[1:3], | |
align_corners=True) for x in logits] | |
scaled_labels = labels | |
else: | |
# Label is downsampled to the same size as logits. | |
assert isinstance(logits, collections.Sequence) | |
scaled_labels = tf.image.resize_nearest_neighbor( | |
labels, | |
preprocess_utils.resolve_shape(logits[0], 4)[1:3], | |
align_corners=True) | |
batch_size = len(logits) | |
num_time = preprocess_utils.resolve_shape(logits[0])[0] | |
reshaped_labels = tf.reshape( | |
scaled_labels, ([batch_size, num_time] + | |
preprocess_utils.resolve_shape(scaled_labels)[1:])) | |
for n, logits_n in enumerate(logits): | |
labels_n = reshaped_labels[n] | |
labels_n = tf.reshape(labels_n, shape=[-1]) | |
not_ignore_mask = tf.to_float(tf.not_equal(labels_n, | |
ignore_label)) * loss_weight | |
num_classes_n = tf.shape(logits_n)[-1] | |
one_hot_labels = slim.one_hot_encoding( | |
labels_n, num_classes_n, on_value=1.0, off_value=0.0) | |
logits_n_flat = tf.reshape(logits_n, shape=[-1, num_classes_n]) | |
if top_k_percent_pixels == 1.0: | |
tf.losses.softmax_cross_entropy( | |
one_hot_labels, | |
logits_n_flat, | |
weights=not_ignore_mask, | |
scope=loss_scope) | |
else: | |
# Only compute the loss for top k percent pixels. | |
# First, compute the loss for all pixels. Note we do not put the loss | |
# to loss_collection and set reduction = None to keep the shape. | |
num_pixels = tf.to_float(tf.shape(logits_n_flat)[0]) | |
pixel_losses = tf.losses.softmax_cross_entropy( | |
one_hot_labels, | |
logits_n_flat, | |
weights=not_ignore_mask, | |
scope='pixel_losses', | |
loss_collection=None, | |
reduction=tf.losses.Reduction.NONE) | |
# Compute the top_k_percent pixels based on current training step. | |
if hard_example_mining_step == 0: | |
# Directly focus on the top_k pixels. | |
top_k_pixels = tf.to_int32(top_k_percent_pixels * num_pixels) | |
else: | |
# Gradually reduce the mining percent to top_k_percent_pixels. | |
global_step = tf.to_float(tf.train.get_or_create_global_step()) | |
ratio = tf.minimum(1.0, global_step / hard_example_mining_step) | |
top_k_pixels = tf.to_int32( | |
(ratio * top_k_percent_pixels + (1.0 - ratio)) * num_pixels) | |
_, top_k_indices = tf.nn.top_k(pixel_losses, | |
k=top_k_pixels, | |
sorted=True, | |
name='top_k_percent_pixels') | |
# Compute the loss for the top k percent pixels. | |
tf.losses.softmax_cross_entropy( | |
tf.gather(one_hot_labels, top_k_indices), | |
tf.gather(logits_n_flat, top_k_indices), | |
weights=tf.gather(not_ignore_mask, top_k_indices), | |
scope=loss_scope) | |
pred_n = tf.argmax(logits_n, axis=-1, output_type=tf.int32)[ | |
..., tf.newaxis] | |
labels_n = labels[n * num_time: (n + 1) * num_time] | |
miou = eval_utils.calculate_multi_object_miou_tf(pred_n, labels_n) | |
tf.summary.scalar('miou', miou) | |
def get_model_init_fn(train_logdir, | |
tf_initial_checkpoint, | |
initialize_last_layer, | |
last_layers, | |
ignore_missing_vars=False): | |
"""Gets the function initializing model variables from a checkpoint. | |
Args: | |
train_logdir: Log directory for training. | |
tf_initial_checkpoint: TensorFlow checkpoint for initialization. | |
initialize_last_layer: Initialize last layer or not. | |
last_layers: Last layers of the model. | |
ignore_missing_vars: Ignore missing variables in the checkpoint. | |
Returns: | |
Initialization function. | |
""" | |
if tf_initial_checkpoint is None: | |
tf.logging.info('Not initializing the model from a checkpoint.') | |
return None | |
if tf.train.latest_checkpoint(train_logdir): | |
tf.logging.info('Ignoring initialization; other checkpoint exists') | |
return None | |
tf.logging.info('Initializing model from path: %s', tf_initial_checkpoint) | |
# Variables that will not be restored. | |
exclude_list = ['global_step'] | |
if not initialize_last_layer: | |
exclude_list.extend(last_layers) | |
variables_to_restore = slim.get_variables_to_restore(exclude=exclude_list) | |
if variables_to_restore: | |
return slim.assign_from_checkpoint_fn( | |
tf_initial_checkpoint, | |
variables_to_restore, | |
ignore_missing_vars=ignore_missing_vars) | |
return None | |