// Copyright (c) 2019 PaddlePaddle 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. #pragma once #include #include #include #include "lite/backends/x86/math/blas.h" #include "lite/core/kernel.h" #include "lite/core/op_lite.h" #include "lite/core/op_registry.h" #include "lite/fluid/eigen.h" #include "lite/operators/activation_ops.h" namespace paddle { namespace lite { namespace kernels { namespace x86 { enum ActBwdOpFwdDeps { kNoDeps = 0x00, // Do not need any forward input/output kDepX = 0x01, // Only need forward input X kDepOut = 0x02, // Only need forward output Out // Never add kDepXOut, because Out can be always calculated // by forward input X in backward part. // FIXME(zjl): but in MKLDNN abs, X and Out are all needed... // Developers should not rely on this enum value! kDepXOut = 0x03 }; template struct BaseActivationFunctor { using ELEMENT_TYPE = T; using AttrPair = std::vector>; AttrPair GetAttrs() { return AttrPair(); } /* NOTE(*): Output reuse X memory if X is not dependented by its Gradient. For example, sigmoid op's gradient didn't involve x, so its output can reuse input memory. But abs op's gradient use x, it can not be inplaced. gradient did use x. */ bool Inplace() const { return false; } }; template bool Activate(const lite::Tensor* X, lite::Tensor* Out) { using T = typename Functor::ELEMENT_TYPE; auto place = lite::fluid::EigenDeviceType(); CHECK_OR_FALSE(X) CHECK_OR_FALSE(Out) auto x = lite::fluid::EigenVector::Flatten(*X); auto out = lite::fluid::EigenVector::Flatten(*Out); Functor()(place, x, out); return true; } // square(x) = x^2 template struct SquareFunctor : public BaseActivationFunctor { template void operator()(Device d, X x, Out out) const { out.device(d) = x.square(); } }; template class SquareCompute : public KernelLite { public: using param_t = operators::ActivationParam; void Run() override { auto& param = *param_.get_mutable(); param.Out->template mutable_data(); Activate>(param.X, param.Out); } virtual ~SquareCompute() = default; }; // relu(x) = max(x, 0) template struct ReluFunctor : public BaseActivationFunctor { template void operator()(Device d, X x, Out out) const { out.device(d) = x.cwiseMax(static_cast(0)); } }; template class ReluCompute : public KernelLite { public: using param_t = operators::ActivationParam; void Run() override { auto& param = *param_.get_mutable(); param.Out->template mutable_data(); Activate>(param.X, param.Out); } virtual ~ReluCompute() = default; }; // tanh(x) = (exp(x) - exp(-x)) / (exp(x) + exp(-x)) template struct TanhFunctor : public BaseActivationFunctor { template void operator()(Device d, X x, Out out) const { out.device(d) = x.tanh(); } }; template class TanhCompute : public KernelLite { public: using param_t = operators::ActivationParam; void Run() override { auto& param = *param_.get_mutable(); param.Out->template mutable_data(); Activate>(param.X, param.Out); } virtual ~TanhCompute() = default; }; // gelu(x) = 0.5 * x * (1 + erf(x / sqrt(2))) template struct GeluFunctor : public BaseActivationFunctor { template void operator()(Device d, X x, Out out) const { // Because the execute or device context can not be deliver here, it keep the // marco for NVCC. #if defined(PADDLE_WITH_MKLML) && !defined(_WIN32) && !defined(__APPLE__) && \ !defined(__OSX__) && !defined(PADDLE_WITH_CUDA) auto x_data = x.data(); auto out_data = out.data(); int n = std::min(x.size(), out.size()); std::memset(out_data, 0, n * sizeof(T)); paddle::lite::x86::math::CBlas::AXPY( n, static_cast(M_SQRT1_2), x_data, 1, out_data, 1); paddle::lite::x86::math::CBlas::VMERF(n, out_data, out_data, VML_LA); for (int i = 0; i < n; i++) { out_data[i] += static_cast(1); } paddle::lite::x86::math::CBlas::VMUL(n, x_data, out_data, out_data); for (int i = 0; i < n; i++) { out_data[i] *= static_cast(0.5); } #else auto temp = (x * static_cast(M_SQRT1_2)).erf(); out.device(d) = x * static_cast(0.5) * (static_cast(1) + temp); #endif } }; template class GeluCompute : public KernelLite { public: using param_t = operators::ActivationParam; void Run() override { auto& param = *param_.get_mutable(); param.Out->template mutable_data(); Activate>(param.X, param.Out); } virtual ~GeluCompute() = default; }; // softsign(x) = x / (1 + |x|) template struct SoftsignFunctor : public BaseActivationFunctor { template void operator()(Device d, X x, Out out) { out.device(d) = x / (static_cast(1) + x.abs()); } }; template class SoftsignCompute : public KernelLite { public: using param_t = operators::ActivationParam; void Run() override { // auto& context = ctx_->As(); auto& param = *param_.get_mutable(); param.Out->template mutable_data(); Activate>(param.X, param.Out); } virtual ~SoftsignCompute() = default; }; } // namespace x86 } // namespace kernels } // namespace lite } // namespace paddle