Commit 0c1682cd authored by Vishnu Banna's avatar Vishnu Banna
Browse files

addressing round one comments

parent 09788b47
...@@ -177,7 +177,7 @@ class YoloLossBase(object, metaclass=abc.ABCMeta): ...@@ -177,7 +177,7 @@ class YoloLossBase(object, metaclass=abc.ABCMeta):
for predictions. for predictions.
""" """
(loss, box_loss, conf_loss, class_loss, mean_loss, iou, pred_conf, ind_mask, (loss, box_loss, conf_loss, class_loss, mean_loss, iou, pred_conf, ind_mask,
grid_mask) = self.call(true_counts, inds, y_true, boxes, classes, y_pred) grid_mask) = self._compute_loss(true_counts, inds, y_true, boxes, classes, y_pred)
# Temporary metrics # Temporary metrics
box_loss = tf.stop_gradient(0.05 * box_loss / self._iou_normalizer) box_loss = tf.stop_gradient(0.05 * box_loss / self._iou_normalizer)
...@@ -185,9 +185,10 @@ class YoloLossBase(object, metaclass=abc.ABCMeta): ...@@ -185,9 +185,10 @@ class YoloLossBase(object, metaclass=abc.ABCMeta):
# Metric compute using done here to save time and resources. # Metric compute using done here to save time and resources.
sigmoid_conf = tf.stop_gradient(tf.sigmoid(pred_conf)) sigmoid_conf = tf.stop_gradient(tf.sigmoid(pred_conf))
iou = tf.stop_gradient(iou) iou = tf.stop_gradient(iou)
avg_iou = loss_utils.avgiou( avg_iou = loss_utils.average_iou(
loss_utils.apply_mask(tf.squeeze(ind_mask, axis=-1), iou)) loss_utils.apply_mask(tf.squeeze(ind_mask, axis=-1), iou))
avg_obj = loss_utils.avgiou(tf.squeeze(sigmoid_conf, axis=-1) * grid_mask) avg_obj = loss_utils.average_iou(
tf.squeeze(sigmoid_conf, axis=-1) * grid_mask)
return (loss, box_loss, conf_loss, class_loss, mean_loss, return (loss, box_loss, conf_loss, class_loss, mean_loss,
tf.stop_gradient(avg_iou), tf.stop_gradient(avg_obj)) tf.stop_gradient(avg_iou), tf.stop_gradient(avg_obj))
...@@ -198,13 +199,14 @@ class YoloLossBase(object, metaclass=abc.ABCMeta): ...@@ -198,13 +199,14 @@ class YoloLossBase(object, metaclass=abc.ABCMeta):
... ...
@abc.abstractmethod @abc.abstractmethod
def call(): def _compute_loss(self, true_counts, inds, y_true, boxes, classes, y_pred):
"""The actual logic to apply to the raw model for optimization.""" """The actual logic to apply to the raw model for optimization."""
... ...
def post_path_aggregation(self, loss, ground_truths, predictions): def post_path_aggregation(self, loss, ground_truths, predictions):
"""This method allows for post processing of a loss value after the loss """This method allows for post processing of a loss value after the loss
has been aggregateda across all the FPN levels.""" has been aggregateda across all the FPN levels. The default behavior is to
pass the loss through with no alterations."""
return loss return loss
@abc.abstractmethod @abc.abstractmethod
...@@ -215,6 +217,12 @@ class YoloLossBase(object, metaclass=abc.ABCMeta): ...@@ -215,6 +217,12 @@ class YoloLossBase(object, metaclass=abc.ABCMeta):
@tf.custom_gradient @tf.custom_gradient
def grad_sigmoid(values): def grad_sigmoid(values):
"""
This function scales the gradient as if a signmoid was applied to the
model output. This is used in the Darknet Loss when the choosen box type
is the scaled coordinate type. This function is used to match the propagated
gradient to match that of the Darkent Yolov4 model.
"""
# This is an identity operation that will # This is an identity operation that will
# allow us to add some steps to the back propagation. # allow us to add some steps to the back propagation.
def delta(dy): def delta(dy):
...@@ -244,7 +252,7 @@ class DarknetLoss(YoloLossBase): ...@@ -244,7 +252,7 @@ class DarknetLoss(YoloLossBase):
iou_type="iou", any=True, min_conf=0.25) iou_type="iou", any=True, min_conf=0.25)
return return
def call(self, true_counts, inds, y_true, boxes, classes, y_pred): def _compute_loss(self, true_counts, inds, y_true, boxes, classes, y_pred):
"""Per FPN path loss computation logic.""" """Per FPN path loss computation logic."""
if self._box_type == "scaled": if self._box_type == "scaled":
# Darknet Model Propagates a sigmoid once in back prop so we replicate # Darknet Model Propagates a sigmoid once in back prop so we replicate
...@@ -326,7 +334,7 @@ class DarknetLoss(YoloLossBase): ...@@ -326,7 +334,7 @@ class DarknetLoss(YoloLossBase):
# Compute the sigmoid binary cross entropy for the class maps. # Compute the sigmoid binary cross entropy for the class maps.
class_loss = tf.reduce_mean( class_loss = tf.reduce_mean(
loss_utils.sigmoid_BCE( loss_utils.sigmoid_bce(
tf.expand_dims(true_class, axis=-1), tf.expand_dims(true_class, axis=-1),
tf.expand_dims(pred_class, axis=-1), self._label_smoothing), tf.expand_dims(pred_class, axis=-1), self._label_smoothing),
axis=-1) axis=-1)
...@@ -347,7 +355,7 @@ class DarknetLoss(YoloLossBase): ...@@ -347,7 +355,7 @@ class DarknetLoss(YoloLossBase):
# Compute the sigmoid binary cross entropy for the confidence maps. # Compute the sigmoid binary cross entropy for the confidence maps.
bce = tf.reduce_mean( bce = tf.reduce_mean(
loss_utils.sigmoid_BCE( loss_utils.sigmoid_bce(
tf.expand_dims(true_conf, axis=-1), pred_conf, 0.0), tf.expand_dims(true_conf, axis=-1), pred_conf, 0.0),
axis=-1) axis=-1)
...@@ -394,7 +402,7 @@ class ScaledLoss(YoloLossBase): ...@@ -394,7 +402,7 @@ class ScaledLoss(YoloLossBase):
iou_type=self._loss_type, any=False, min_conf=0.25) iou_type=self._loss_type, any=False, min_conf=0.25)
return return
def call(self, true_counts, inds, y_true, boxes, classes, y_pred): def _compute_loss(self, true_counts, inds, y_true, boxes, classes, y_pred):
"""Per FPN path loss computation logic.""" """Per FPN path loss computation logic."""
# Generate shape constants. # Generate shape constants.
shape = tf.shape(true_counts) shape = tf.shape(true_counts)
...@@ -501,21 +509,23 @@ class ScaledLoss(YoloLossBase): ...@@ -501,21 +509,23 @@ class ScaledLoss(YoloLossBase):
ind_mask, grid_mask) ind_mask, grid_mask)
def post_path_aggregation(self, loss, ground_truths, predictions): def post_path_aggregation(self, loss, ground_truths, predictions):
"""By default the model will have about 3 FPN levels {3, 4, 5}, on
larger model that have more like 4 or 5 FPN levels the loss needs to
be scaled such that the total update is scaled to the same effective
magintude as the model with 3 FPN levels. This helps to prevent gradient
explosions."""
scale = tf.stop_gradient(3 / len(list(predictions.keys()))) scale = tf.stop_gradient(3 / len(list(predictions.keys())))
return loss * scale return loss * scale
def cross_replica_aggregation(self, loss, num_replicas_in_sync): def cross_replica_aggregation(self, loss, num_replicas_in_sync):
"""this method is not specific to each loss path, but each loss type""" """In the scaled loss, the loss is aggregated across replicas via the
sum."""
return loss return loss
LOSSES = {"darknet": DarknetLoss, "scaled": ScaledLoss}
class YoloLoss(object): class YoloLoss(object):
"""This class implements the aggregated loss across paths for the YOLO """This class implements the aggregated loss across paths for the YOLO
model. The class implements the YOLO loss as a factory in order to allow model."""
selection and implementation of new versions of the YOLO loss as the model
is updated in the future.
"""
def __init__(self, def __init__(self,
keys, keys,
...@@ -582,6 +592,8 @@ class YoloLoss(object): ...@@ -582,6 +592,8 @@ class YoloLoss(object):
update_on_repeat: `bool` for whether to replace with the newest or update_on_repeat: `bool` for whether to replace with the newest or
the best value when an index is consumed by multiple objects. the best value when an index is consumed by multiple objects.
""" """
LOSSES = {"darknet": DarknetLoss, "scaled": ScaledLoss}
if use_scaled_loss: if use_scaled_loss:
loss_type = "scaled" loss_type = "scaled"
else: else:
......
...@@ -22,7 +22,7 @@ from official.vision.beta.projects.yolo.modeling.layers import nn_blocks ...@@ -22,7 +22,7 @@ from official.vision.beta.projects.yolo.modeling.layers import nn_blocks
@tf.keras.utils.register_keras_serializable(package='yolo') @tf.keras.utils.register_keras_serializable(package='yolo')
class _IdentityRoute(tf.keras.layers.Layer): class _IdentityRoute(tf.keras.layers.Layer):
def call(self, inputs): # pylint: disable=arguments-differ def call(self, inputs):
return None, inputs return None, inputs
......
...@@ -72,8 +72,10 @@ class YoloLayer(tf.keras.Model): ...@@ -72,8 +72,10 @@ class YoloLayer(tf.keras.Model):
obj_normalizer: `float` for how much to scale loss on the detection map. obj_normalizer: `float` for how much to scale loss on the detection map.
use_scaled_loss: `bool` for whether to use the scaled loss use_scaled_loss: `bool` for whether to use the scaled loss
or the traditional loss. or the traditional loss.
darknet: `bool` for whether to use the DarkNet or PyTorch loss function update_on_repeat: `bool` indicating how you would like to handle repeated
implementation. indexes in a given [j, i] index. Setting this to True will give more
consistent MAP, setting it to falls will improve recall by 1-2% but will
sacrifice some MAP.
pre_nms_points: `int` number of top candidate detections per class before pre_nms_points: `int` number of top candidate detections per class before
NMS. NMS.
label_smoothing: `float` for how much to smooth the loss on the classes. label_smoothing: `float` for how much to smooth the loss on the classes.
......
...@@ -18,7 +18,7 @@ import numpy as np ...@@ -18,7 +18,7 @@ import numpy as np
from official.vision.beta.projects.yolo.ops import (box_ops, math_ops) from official.vision.beta.projects.yolo.ops import (box_ops, math_ops)
@tf.custom_gradient @tf.custom_gradient
def sigmoid_BCE(y, x_prime, label_smoothing): def sigmoid_bce(y, x_prime, label_smoothing):
"""Applies the Sigmoid Cross Entropy Loss Using the same derivative as that """Applies the Sigmoid Cross Entropy Loss Using the same derivative as that
found in the Darknet C library. The derivative of this method is not the same found in the Darknet C library. The derivative of this method is not the same
as the standard binary cross entropy with logits function. as the standard binary cross entropy with logits function.
...@@ -144,7 +144,7 @@ def build_grid(indexes, truths, preds, ind_mask, update=False, grid=None): ...@@ -144,7 +144,7 @@ def build_grid(indexes, truths, preds, ind_mask, update=False, grid=None):
return grid return grid
class GridGenerator(object): class GridGenerator:
"""Grid generator that generates anchor grids that will be used """Grid generator that generates anchor grids that will be used
in to decode the predicted boxes.""" in to decode the predicted boxes."""
...@@ -216,7 +216,7 @@ class GridGenerator(object): ...@@ -216,7 +216,7 @@ class GridGenerator(object):
TILE_SIZE = 50 TILE_SIZE = 50
class PairWiseSearch(object): class PairWiseSearch:
"""This method applies a pairwise search between the ground truth """This method applies a pairwise search between the ground truth
and the labels. The goal is to indicate the locations where the and the labels. The goal is to indicate the locations where the
predictions overlap with ground truth for dynamic ground predictions overlap with ground truth for dynamic ground
...@@ -367,7 +367,7 @@ class PairWiseSearch(object): ...@@ -367,7 +367,7 @@ class PairWiseSearch(object):
tf.stop_gradient(max_iou), tf.stop_gradient(mask)) tf.stop_gradient(max_iou), tf.stop_gradient(mask))
def avgiou(iou): def average_iou(iou):
"""Computes the average intersection over union without counting locations """Computes the average intersection over union without counting locations
where the iou is zero. where the iou is zero.
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment