/*************************************************************************************************** * Copyright (c) 2017 - 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * **************************************************************************************************/ /*! \file \brief This extends the contents of cutlass/functional.h with frequently used activation functions. */ #pragma once #include "cutlass/cutlass.h" #include "cutlass/numeric_types.h" #include "cutlass/constants.h" #include "cutlass/complex.h" #include "cutlass/array.h" #include "cutlass/half.h" #include "cutlass/functional.h" ///////////////////////////////////////////////////////////////////////////////////////////////// namespace cutlass { namespace epilogue { namespace thread { ///////////////////////////////////////////////////////////////////////////////////////////////// template struct LinearCombinationGenericParams { T alpha; ///< scales accumulators T beta; ///< scales source tensor T const *alpha_ptr; ///< pointer to accumulator scalar - if not null, loads it from memory T const *beta_ptr; ///< pointer to source scalar - if not null, loads it from memory // // Methods // CUTLASS_HOST_DEVICE LinearCombinationGenericParams(): alpha(T(1)), beta(T(0)), alpha_ptr(nullptr), beta_ptr(nullptr) { } CUTLASS_HOST_DEVICE LinearCombinationGenericParams( T alpha, T beta = T(0) ): alpha(alpha), beta(beta), alpha_ptr(nullptr), beta_ptr(nullptr) { } CUTLASS_HOST_DEVICE LinearCombinationGenericParams( T const *alpha_ptr, T const *beta_ptr = nullptr ): alpha(0), beta(0), alpha_ptr(alpha_ptr), beta_ptr(beta_ptr) { } }; ///////////////////////////////////////////////////////////////////////////////////////////////// // Identity operator template struct Identity { static const bool kIsHeavy=false; CUTLASS_HOST_DEVICE T operator()(T value) const { return value; } using Params = LinearCombinationGenericParams; CUTLASS_HOST_DEVICE T operator()(T const &value, Params const ¶ms_) const { return this->operator()(value); } }; template struct Identity > { CUTLASS_HOST_DEVICE Array operator()(Array const &value) const { return value; } using Params = LinearCombinationGenericParams; CUTLASS_HOST_DEVICE Array operator()(Array const &value, Params const ¶ms_) const { return this->operator()(value); } }; /// ReLu operator - propagates NaNs /// Always put threshold in the right hand side of max to propagate NaN. template struct ReLu { static const bool kIsHeavy=false; CUTLASS_HOST_DEVICE T operator()(T const & threshold, T value) const { maximum mx; return mx(value, threshold); } CUTLASS_HOST_DEVICE T operator()(T value) const { maximum mx; return mx(value, T(0)); } /// Host-constructable parameters structure using Params = LinearCombinationGenericParams; CUTLASS_HOST_DEVICE T operator()(T value, Params const ¶ms_) const { return this->operator()(value); } }; template struct ReLu> { static const bool kIsHeavy=false; CUTLASS_HOST_DEVICE Array operator()(T const & threshold, Array const &frag) const { maximum > mx; return mx(frag, threshold); } CUTLASS_HOST_DEVICE Array operator()(Array const &frag) const { maximum > mx; return mx(frag, T(0)); } /// Host-constructable parameters structure using Params = LinearCombinationGenericParams; CUTLASS_HOST_DEVICE Array operator()(Array const &frag, Params const ¶ms_) const { return this->operator()(frag); } }; // Leaky Relu operator template struct LeakyReLU { struct Params: LinearCombinationGenericParams { T leaky_alpha; ///< leaky_alpha // Methods using LinearCombinationGenericParams::LinearCombinationGenericParams; CUTLASS_HOST_DEVICE Params(): LinearCombinationGenericParams(), leaky_alpha(T(1)) {} CUTLASS_HOST_DEVICE Params( T alpha, T beta, T leaky_alpha = T(1) ): LinearCombinationGenericParams(alpha, beta), leaky_alpha(leaky_alpha) {} }; CUTLASS_HOST_DEVICE T operator()(T const &value, T const & alpha_recip) const { T res = value > T(0) ? value : value * alpha_recip; return res; } CUTLASS_HOST_DEVICE T operator()(T const &value, Params const ¶ms_) const { this->operator()(value, params_.leaky_alpha); } }; template struct LeakyReLU > { struct Params: LinearCombinationGenericParams { T leaky_alpha; ///< leaky_alpha using LinearCombinationGenericParams::LinearCombinationGenericParams; // Methods CUTLASS_HOST_DEVICE Params(): LinearCombinationGenericParams(), leaky_alpha(T(1)) {} CUTLASS_HOST_DEVICE Params( T alpha, T beta, T leaky_alpha = T(1) ): LinearCombinationGenericParams(alpha, beta), leaky_alpha(leaky_alpha) {} }; CUTLASS_HOST_DEVICE Array operator()(Array const &value, T const & alpha_recip) const { Array y; LeakyReLU leaky_op; CUTLASS_PRAGMA_UNROLL for (int i = 0; i < int(value.size()); ++i) { y[i] = leaky_op(value[i], alpha_recip); } return y; } CUTLASS_HOST_DEVICE Array operator()(Array const &value, Params const ¶ms_) const { return this->operator()(value, params_.leaky_alpha); } }; // Tanh operator template struct Tanh { CUTLASS_HOST_DEVICE T operator()(T const &scalar) const { return fast_tanh(scalar); } using Params = LinearCombinationGenericParams; CUTLASS_HOST_DEVICE T operator()(T const &scalar, Params const ¶ms_) const { return this->operator()(scalar); } }; template struct Tanh > { CUTLASS_HOST_DEVICE Array operator()(Array const &value) const { Array y; Tanh tanh_op; CUTLASS_PRAGMA_UNROLL for (int i = 0; i < N; ++i) { y[i] = tanh_op(value[i]); } return y; } using Params = LinearCombinationGenericParams; CUTLASS_HOST_DEVICE Array operator()(Array const &value, Params const ¶ms_) const { return this->operator()(value); } }; template struct Tanh> { using T = half_t; CUTLASS_HOST_DEVICE Array operator()(Array const& z) const { fast_tanh_op> tanh; return tanh(z); } using Params = LinearCombinationGenericParams; CUTLASS_HOST_DEVICE Array operator()(Array const &value, Params const ¶ms_) const { return this->operator()(value); } }; // Sigmoid operator template struct Sigmoid { CUTLASS_HOST_DEVICE T operator()(T const &scalar) const { return T(1) / (T(1) + fast_exp(-scalar)); } using Params = LinearCombinationGenericParams; CUTLASS_HOST_DEVICE T operator()(T const &scalar, Params const ¶ms_) const { return this->operator()(scalar); } }; template struct Sigmoid > { CUTLASS_HOST_DEVICE Array operator()(Array const &value) const { Array y; Sigmoid sigmoid_op; CUTLASS_PRAGMA_UNROLL for (int i = 0; i < N; ++i) { y[i] = sigmoid_op(value[i]); } return y; } using Params = LinearCombinationGenericParams; CUTLASS_HOST_DEVICE Array operator()(Array const &value, Params const ¶ms_) const { return this->operator()(value); } }; template struct Sigmoid> { using T = half_t; CUTLASS_HOST_DEVICE Array operator()(Array const& z) const { plus> add; #if defined(CUTLASS_USE_TANH_FOR_SIGMOID) multiplies> mul; fast_tanh_op> tanh; return mul(add(tanh(mul(z, cutlass::constants::half())), cutlass::constants::one()), cutlass::constants::half()); #else divides> div; negate> neg; fast_exp_op> fast_exp; return div(cutlass::constants::one(), add(cutlass::constants::one(), fast_exp(neg(z)))); #endif } using Params = LinearCombinationGenericParams; CUTLASS_HOST_DEVICE Array operator()(Array const &z, Params const ¶ms_) const { return this->operator()(z); } }; // SiLu (swish) operator introduced by Elfwing et al. in the following paper // "Sigmoid-Weighted Linear Units for Neural Network Function Approximation in Reinforcement Learning" (2017) // https://arxiv.org/pdf/1702.03118.pdf // It is used in EfficientNet and YOLOv5, for example. // Reference: https://pytorch.org/docs/stable/generated/torch.nn.SiLU.html template struct SiLu { CUTLASS_HOST_DEVICE T operator()(T const &scalar) const { Sigmoid sigmoid; return scalar * sigmoid(scalar); } using Params = LinearCombinationGenericParams; CUTLASS_HOST_DEVICE T operator()(T const &scalar, Params const ¶ms_) const { return this->operator()(scalar); } }; template struct SiLu> { CUTLASS_HOST_DEVICE Array operator()(Array const &value) const { Sigmoid> sigmoid_op; multiplies> mul; return mul(value, sigmoid_op(value)); } using Params = LinearCombinationGenericParams; CUTLASS_HOST_DEVICE Array operator()(Array const &value, Params const ¶ms_) const { return this->operator()(value); } }; // Hardswish operator introduced by Howard et al. in the following paper // "Searching for MobileNetV3" (2019) // https://arxiv.org/pdf/1905.02244.pdf // It is used in models based on MobilenetNetV3. // Reference: https://pytorch.org/docs/stable/generated/torch.nn.Hardswish.html template struct HardSwish { CUTLASS_HOST_DEVICE T operator()(T const &x) const { minimum mn; maximum mx; T relu6 = mn(mx(x + T(3), T(0)), T(6)); return x * relu6 / T(6); } using Params = LinearCombinationGenericParams; CUTLASS_HOST_DEVICE T operator()(T const &x, Params const ¶ms_) const { return this->operator()(x); } }; template <> struct HardSwish { using T = float; CUTLASS_HOST_DEVICE T operator()(T const &x) const { minimum mn; maximum mx; T relu6 = mn(mx(x + T(3), T(0)), T(6)); return x * relu6 * 0.16666667f; } using Params = LinearCombinationGenericParams; CUTLASS_HOST_DEVICE T operator()(T const &x, Params const ¶ms_) const { return this->operator()(x); } }; template struct HardSwish > { CUTLASS_HOST_DEVICE Array operator()(Array const &value) const { Array y; HardSwish hardswish_op; CUTLASS_PRAGMA_UNROLL for (int i = 0; i < N; ++i) { y[i] = hardswish_op(value[i]); } return y; } using Params = LinearCombinationGenericParams; CUTLASS_HOST_DEVICE Array operator()(Array const &x, Params const ¶ms_) const { return this->operator()(x); } }; template struct HardSwish > { using T = half_t; CUTLASS_HOST_DEVICE Array operator()(Array const &value) const { minimum > mn; maximum > mx; multiplies > mul; plus > add; return mul(mul(mn(mx(add(value, T(3)), T(0)), T(6)), value), T(0.16666667f)); } using Params = LinearCombinationGenericParams; CUTLASS_HOST_DEVICE Array operator()(Array const &x, Params const ¶ms_) const { return this->operator()(x); } }; // // GELU function definitions implemented as described by // Hendrycks, D., and Gimpel, K. in // "Gaussian Error Linear Units (GELUs)." (2020) // https://arxiv.org/pdf/1606.08415.pdf // // Floating-point constants are Taylor coefficients described in the paper. // // GELU operator template struct GELU { CUTLASS_HOST_DEVICE T operator()(T const &scalar) const { return T(cutlass::constants::half() * scalar * (cutlass::constants::one() + (T)erff((float)(scalar / cutlass::constants::root_two())))); } using Params = LinearCombinationGenericParams; CUTLASS_HOST_DEVICE T operator()(T const &scalar, Params const ¶ms_) const { return this->operator()(scalar); } }; template <> struct GELU { CUTLASS_HOST_DEVICE float operator()(float const &scalar) const { return cutlass::constants::half() * scalar * (cutlass::constants::one() + erff( scalar / cutlass::constants::root_two() )); } using Params = LinearCombinationGenericParams; CUTLASS_HOST_DEVICE float operator()(float const &scalar, Params const ¶ms_) const { return this->operator()(scalar); } }; template <> struct GELU { CUTLASS_HOST_DEVICE double operator()(double const &scalar) const { return cutlass::constants::half() * scalar * (cutlass::constants::one() + erf( scalar / cutlass::constants::root_two() )); } using Params = LinearCombinationGenericParams; CUTLASS_HOST_DEVICE double operator()(double const &scalar, Params const ¶ms_) const { return this->operator()(scalar); } }; template struct GELU > { CUTLASS_HOST_DEVICE Array operator()(Array const &value) const { Array y; GELU gelu_op; CUTLASS_PRAGMA_UNROLL for (int i = 0; i < N; ++i) { y[i] = gelu_op(value[i]); } return y; } using Params = LinearCombinationGenericParams; CUTLASS_HOST_DEVICE Array operator()(Array const &value, Params const ¶ms_) const { return this->operator()(value); } }; // GELU operator implemented using the Taylor series approximation template struct GELU_taylor { static const bool kIsHeavy=true; CUTLASS_HOST_DEVICE T operator()(T const &z) const { T k0 = T(0.7978845608028654); T k1 = T(0.044715); return T(cutlass::constants::half() * z * (cutlass::constants::one() + fast_tanh(k0 * z * (cutlass::constants::one() + k1 * z * z)))); } using Params = LinearCombinationGenericParams; CUTLASS_HOST_DEVICE T operator()(T const &scalar, Params const ¶ms_) const { return this->operator()(scalar); } }; template struct GELU_taylor > { static const bool kIsHeavy=true; CUTLASS_HOST_DEVICE Array operator()(Array const &z) const { using T = half_t; Array y; half_t k0 = half_t(0.7978845608028654); half_t k1 = half_t(0.044715); multiply_add> fma; multiplies> mul; plus> add; fast_tanh_op> tanh; Array u = mul(mul(k0, z), fma(mul(k1, z), z, cutlass::constants::one())); y = mul(mul(z, cutlass::constants::half()), add(cutlass::constants::one(), tanh(u))); return y; } using Params = LinearCombinationGenericParams; CUTLASS_HOST_DEVICE Array operator()(Array const &value, Params const ¶ms_) const { return this->operator()(value); } }; template struct GELU_taylor > { static const bool kIsHeavy=true; CUTLASS_HOST_DEVICE Array operator()(Array const &value) const { Array y; GELU_taylor gelu_op; CUTLASS_PRAGMA_UNROLL for (int i = 0; i < N; ++i) { y[i] = gelu_op(value[i]); } return y; } using Params = LinearCombinationGenericParams; CUTLASS_HOST_DEVICE Array operator()(Array const &value, Params const ¶ms_) const { return this->operator()(value); } }; /// Computes backwards pass for GELU operator assuming d_t is the layer gradient and /// z is computed from the forward pass. template struct dGELU { CUTLASS_HOST_DEVICE T operator()(T const &d_t, T const &z) const { T k0 = T(0.7978845608028654); T k1 = T(0.044715); T k2 = T(0.1070322243); T tanh_out = fast_tanh(k0 * z * (1 + k1 * z * z)); T ff = constants::half() * z * ((1 - tanh_out * tanh_out) * (k0 + k2 * z * z)) + constants::half() * (1 + tanh_out); return ff * d_t; } }; template struct dGELU > { CUTLASS_HOST_DEVICE Array operator()(Array const &d_t, Array const &z) const { Array y; dGELU gelu_op; CUTLASS_PRAGMA_UNROLL for (int i = 0; i < N; ++i) { y[i] = gelu_op(d_t[i], z[i]); } return y; } }; ///////////////////////////////////////////////////////////////////////////////////////////////// } // namespace thread } // namespace epilogue } // namespace cutlass /////////////////////////////////////////////////////////////////////////////////////////////////