// // UnaryGrad.cpp // MNN // // Created by MNN on 2019/05/25. // Copyright © 2018, Alibaba Group Holding Limited // #include "OpGrad.hpp" #include "core/Macro.h" #define MNN_PI 3.14159265358979323846 using namespace std; namespace MNN { using namespace MNN::Express; class UnaryGrad : public OpGrad { public: virtual std::vector onGrad(Express::EXPRP expr, const std::vector& backwardOutput) override { std::unique_ptr forwardOp(expr->get()->UnPack()); auto outputDiff = backwardOutput[0]; auto input = expr->inputs()[0]; std::vector res(1, nullptr); std::vector output{Variable::create(expr, 0)}; switch (forwardOp->main.AsUnaryOp()->opType) { case MNN::UnaryOpOperation_LOG1P: { // d log(1+x) = 1/(1+x) * dx = dx / (1+x) auto oneConst = _Const(1.0f, {}, NHWC); auto addOne = _Add(input, oneConst); res[0] = _Divide(outputDiff, addOne); break; } case MNN::UnaryOpOperation_EXP: { // d Exp(x) = Exp(x) * dx res[0] = _Multiply(outputDiff, output[0]); break; } case MNN::UnaryOpOperation_LOG: { // d Log(x) = dx / x res[0] = _Divide(outputDiff, input); break; } case MNN::UnaryOpOperation_COS: { // d Sin(x) = -dx * Sin(x) res[0] = _Negative(outputDiff) * _Sin(input); break; } case MNN::UnaryOpOperation_SIN: { // d Sin(x) = dx * Cos(x) res[0] = outputDiff * _Cos(input); break; } case MNN::UnaryOpOperation_ABS: { // d Abs(x) = dx * (x > 0 ? 1 : -1) res[0] = outputDiff * _Sign(input); break; } case MNN::UnaryOpOperation_NEG: { // d (-x) = - dx res[0] = _Negative(outputDiff); break; } case MNN::UnaryOpOperation_SQRT: { // d (-sqrt(x)) = 0.5 / sqrt(x) * dx auto oneConst = _Const(0.5f, {}, NHWC); auto mul = _Multiply(outputDiff, oneConst); res[0] = OpGrad::divideAvoidZero(mul, output[0]); break; } case MNN::UnaryOpOperation_SQUARE: { // d (x^2) = (x*dx + x*dx) auto mul = _Multiply(input, outputDiff); res[0] = _Add(mul, mul); break; } case MNN::UnaryOpOperation_SIGMOID: { auto grad = OpGrad::get(OpType_Sigmoid); res[0] = grad->onGrad(expr, backwardOutput)[0]; break; } case MNN::UnaryOpOperation_TANH: { auto grad = OpGrad::get(OpType_TanH); res[0] = grad->onGrad(expr, backwardOutput)[0]; break; } case MNN::UnaryOpOperation_RSQRT: { // d (x^(-1/2)) = -1/2 * x^(-3/2) * dx res[0] = _Scalar(-0.5f) * _Pow(input, _Scalar(-1.5f)) * outputDiff; break; } case MNN::UnaryOpOperation_TAN: { // d tan(x) = 1 / (cos(x))^2 * dx res[0] = _Scalar(1.0f) / _Square(_Cos(input)) * outputDiff; break; } case MNN::UnaryOpOperation_ASIN: { // d asin(x) = 1 / sqrt(1 - x^2) * dx res[0] = _Scalar(1.0f) / _Sqrt(_Scalar(1.0f) - _Square(input)) * outputDiff; break; } case MNN::UnaryOpOperation_ACOS: { // d acos(x) = -1 / sqrt(1 - x^2) * dx res[0] = _Scalar(-1.0f) / _Sqrt(_Scalar(1.0f) - _Square(input)) * outputDiff; break; } case MNN::UnaryOpOperation_ATAN: { // d atan(x) = 1 / (1 + x^2) * dx res[0] = _Scalar(1.0f) / (_Scalar(1.0f) + _Square(input)) * outputDiff; break; } case MNN::UnaryOpOperation_RECIPROCAL: { // d x^-1 = - x^-2 * dx res[0] = _Negative(_Pow(input, _Scalar(-2.0f))) * outputDiff; break; } case MNN::UnaryOpOperation_ACOSH: { // d acosh(x) = 1 / sqrt(x^2 - 1) * dx res[0] = _Scalar(1.0f) / _Sqrt(_Square(input) - _Scalar(1.0f)) * outputDiff; break; } case MNN::UnaryOpOperation_SINH: { // d sinh(x) = cosh(x) * dx res[0] = _Cosh(input) * outputDiff; break; } case MNN::UnaryOpOperation_COSH: { // d cosh(x) = sinh(x) * dx res[0] = _Sinh(input) * outputDiff; break; } case MNN::UnaryOpOperation_ASINH: { // d asinh(x) = 1 / sqrt(x^2 + 1) * dx res[0] = _Scalar(1.0f) / _Sqrt(_Square(input) + _Scalar(1.0f)) * outputDiff; break; } case MNN::UnaryOpOperation_ATANH: { // d atanh(x) = 1 / (1 - x^2) * dx res[0] = _Scalar(1.0f) / (_Scalar(1.0f) - _Square(input)) * outputDiff; break; } case MNN::UnaryOpOperation_ERF: { // d erf(x) = 2 / sqrt(pi) * exp(- x^2) * dx res[0] = _Scalar(2.0f) / _Sqrt(_Scalar(float(MNN_PI))) * _Exp(_Negative(_Square(input))) * outputDiff; break; } case MNN::UnaryOpOperation_ERFC: { // d erfc(x) = -2 / sqrt(pi) * exp(- x^2) * dx res[0] = _Scalar(-2.0f) / _Sqrt(_Scalar(float(MNN_PI))) * _Exp(_Negative(_Square(input))) * outputDiff; break; } case MNN::UnaryOpOperation_ERFINV: { // d erfinv(x) = sqrt(pi) / 2 * exp(erfinv(x)^2) * dx res[0] = _Sqrt(_Scalar(float(MNN_PI))) / _Scalar(2.0f) * _Exp(_Square(_Erfinv(input))) * outputDiff; break; } case MNN::UnaryOpOperation_EXPM1: { // d expm1(x) = exp(x) * dx res[0] = _Exp(input) * outputDiff; break; } case MNN::UnaryOpOperation_HARDSWISH: { // d hardswish(x) = (relu6(x+3) + x * relu6'(x+3)) / 6 * dx auto inputp3 = input + _Scalar(3.0f); auto mask0 = _Cast(_Greater(inputp3, _Scalar(0.0f))); auto mask1 = _Cast(_Less(inputp3, _Scalar(6.0f))); auto relu6Xp3Grad = mask0 * mask1; res[0] = (_Relu6(inputp3) + input * relu6Xp3Grad) / _Scalar(6.0f) * outputDiff; break; } case MNN::UnaryOpOperation_GELU: case MNN::UnaryOpOperation_GELU_STANDARD: { // d gelu(x) = 0.5 * ( (1 + erf(x / sqrt(2))) + x * (erf'(x / sqrt(2)) / sqrt(2)) ) * dx auto const05 = _Scalar(0.5f); auto inputx = input / _Sqrt(_Scalar(2.0f)); auto part1 = _Scalar(1.0f) + _Erf(inputx); auto erfGrad = _Scalar(2.0f) / _Sqrt(_Scalar(float(MNN_PI))) * _Exp(_Negative(_Square(inputx))); auto part2 = input * erfGrad / _Sqrt(_Scalar(2.0f)); res[0] = const05 * (part1 + part2) * outputDiff; break; } default: MNN_ERROR("Can't grad for unary: %d\n", forwardOp->main.AsUnaryOp()->opType); return res; } return res; } }; class SigmoidGrad : public OpGrad { public: virtual std::vector onGrad(Express::EXPRP expr, const std::vector& backwardOutput) override { std::vector result(1, nullptr); auto outputDiff = backwardOutput[0]; std::vector output{Variable::create(expr, 0)}; // y = (1/(1+e(-x))) , dy = y(1-y) * dx = (y - y*y)*dx auto mul = _Multiply(output[0], output[0]); auto sub = _Subtract(output[0], mul); auto grad = _Multiply(sub, outputDiff); result[0] = grad; return result; } }; class TanhGrad : public OpGrad { public: virtual std::vector onGrad(Express::EXPRP expr, const std::vector& backwardOutput) override { std::vector result{nullptr}; std::vector output{Variable::create(expr, 0)}; auto outputDiff = backwardOutput[0]; // d tanh(x) = (1-tanh(x)^2)dx result[0] = (_Const(1.0f, {}, NCHW) - _Square(output[0])) * outputDiff; return result; } }; static void _create() { static UnaryGrad _c; static SigmoidGrad _s; static TanhGrad _t; OpGrad::insert(OpType_UnaryOp, &_c); OpGrad::insert(OpType_Sigmoid, &_s); OpGrad::insert(OpType_TanH, &_t); } REGISTER_GRAD(UnaryGrad_cpp, _create); };