# coding=utf-8 # Copyright 2021 The Deeplab2 Authors. # # 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. """Drop path operation. This scripts implements the drop path operation, proposed in Gao Huang, Yu Sun, Zhuang Liu, Daniel Sedra, Kilian Weinberger, Deep Networks with Stochastic Depth. In ECCV, 2016. """ import tensorflow as tf def get_drop_path_keep_prob(keep_prob_for_last_stage, schedule, current_stage, num_stages): """Gets drop path keep probability for current stage. Args: keep_prob_for_last_stage: A float, the drop path keep probability for last stage. This flag is used in conjunction with the flag `schedule`, as they together determine drop path keep probability for the other stages. schedule: A string, the drop path schedule. Currently, we support 'constant': use the same drop path keep probability for all stages, and 'linear': linearly decrease the drop path keep probability from 1.0 at 0-th stage (or STEM) to `keep_prob_for_last_stage` at last stage. current_stage: An integer, current stage number. num_stages: An integer, the number of stages. Returns: The drop path keep probability for the current stage. Raises: ValueError: If schedule is not supported. """ if schedule == 'constant': return keep_prob_for_last_stage elif schedule == 'linear': return 1.0 - (1.0 - keep_prob_for_last_stage) * current_stage / num_stages else: raise ValueError('Unexpected schedule %s.' % schedule) def generate_drop_path_random_mask(input_tensor, drop_path_keep_prob): """Generates a random mask for drop path. This function generates a random mask for training models with drop path. Each scalar in the output indicates whether the block or path will be kept. The scalars are scaled with (1.0 / drop_path_keep_prob) so that the output will have the same expectation no mather what the drop_path_keep_prob is. Reference: "Deep Networks with Stochastic Depth" https://arxiv.org/pdf/1603.09382.pdf Args: input_tensor: An input [batch_size, n_1, n_2, ..., n_k] tensor. drop_path_keep_prob: A float, the keep probability for dropping path. Returns: binary_tensor: A [batch_size, 1, 1, ..., 1] tensor with the same dtype as the input_tensor. """ binary_tensor = None if drop_path_keep_prob < 1.0: input_shape = input_tensor.get_shape().as_list() random_tensor_shape = [input_shape[0]] + [1] * (len(input_shape) - 1) random_tensor = drop_path_keep_prob random_tensor += tf.random.uniform( random_tensor_shape, dtype=input_tensor.dtype) binary_tensor = tf.math.divide(tf.floor(random_tensor), drop_path_keep_prob) return binary_tensor class DropPath(tf.keras.layers.Layer): """Drop path layer. For details, please see the original paper listed below. Gao Huang, Yu Sun, Zhuang Liu, Daniel Sedra, Kilian Weinberger, Deep Networks with Stochastic Depth. In ECCV, 2016. """ def __init__(self, drop_path_keep_prob=1.0, name=None): """Initializes a drop path layer. Args: drop_path_keep_prob: A float, the keep probability for dropping path. name: An optional string specifying the operation name. Rasies: ValueError: If drop_path_keep_prob is <= 0 or > 1. """ super(DropPath, self).__init__(name=name) self._drop_path_keep_prob = drop_path_keep_prob if self._drop_path_keep_prob <= 0 or self._drop_path_keep_prob > 1.0: raise ValueError('drop_path_keep_prob not valid. Got %f.' % self._drop_path_keep_prob) def call(self, input_tensor, training=False): """Performs a forward pass. Args: input_tensor: An input tensor of type tf.Tensor with shape [batch, height, width, channels]. training: A boolean flag indicating whether training behavior should be used (default: False). Returns: The output tensor. """ if self._drop_path_keep_prob == 1.0 or not training: return input_tensor drop_path_random_mask = generate_drop_path_random_mask( input_tensor, self._drop_path_keep_prob) if drop_path_random_mask is not None: input_tensor = input_tensor * drop_path_random_mask return input_tensor def get_config(self): config = { 'drop_path_keep_prob': self._drop_path_keep_prob, } base_config = super(DropPath, self).get_config() return dict(list(base_config.items()) + list(config.items()))