// // ReduceGrad.cpp // MNN // // Created by MNN on 2019/05/24. // Copyright © 2018, Alibaba Group Holding Limited // #include "OpGrad.hpp" using namespace std; namespace MNN { using namespace MNN::Express; class ReduceGrad : public OpGrad { public: virtual std::vector onGrad(Express::EXPRP expr, const std::vector& backwardOutput) override { std::vector result; auto inputs = expr->inputs(); result.resize(inputs.size()); std::unique_ptr forwardOp(expr->get()->UnPack()); std::vector reductionDims = forwardOp->main.AsReductionParam()->dim; auto keepDim = forwardOp->main.AsReductionParam()->keepDims; if (inputs.size() > 1) { reductionDims.clear(); auto ptr = inputs[1]->readMap(); auto shape = inputs[1]->getInfo(); for (int i = 0; i < shape->size; ++i) { reductionDims.emplace_back(ptr[i]); } } if (reductionDims.empty()) { auto shape = inputs[0]->getInfo(); for (int i = 0; i < shape->dim.size(); ++i) { reductionDims.emplace_back(i); } } VARP mask = _ZerosLike(inputs[0]) + _Scalar(1.0f); auto outputDiff = backwardOutput[0]; // implement other reduction op's grad below if (forwardOp->main.AsReductionParam()->operation == ReductionType_SUM) { // do not need to modify grads, just copy them, so, pass } if (forwardOp->main.AsReductionParam()->operation == ReductionType_MEAN) { auto gradCount = _Size(outputDiff); auto inputCount = _Size(inputs[0]); outputDiff = _Multiply(outputDiff, _Cast(gradCount) / _Cast(inputCount)); } if (forwardOp->main.AsReductionParam()->operation == ReductionType_MAXIMUM) { auto output = Variable::create(expr); if (!keepDim) { output = _Unsqueeze(output, reductionDims); } mask = _Sign(inputs[0] - output) + _Scalar(1.0f); mask = mask / _ReduceSum(mask); } if (forwardOp->main.AsReductionParam()->operation == ReductionType_MINIMUM) { auto output = Variable::create(expr); if (!keepDim) { output = _Unsqueeze(output, reductionDims); } mask = _Sign(output - inputs[0]) + _Scalar(1.0f); mask = mask / _ReduceSum(mask); } if (forwardOp->main.AsReductionParam()->operation == ReductionType_PROD) { auto output = Variable::create(expr); if (!keepDim) { output = _Unsqueeze(output, reductionDims); } mask = output / inputs[0]; } // this should be common operations, to expand grads to inputs shape if (!keepDim) { outputDiff = _Unsqueeze(outputDiff, reductionDims); } result[0] = mask * outputDiff; return result; } }; class FillGrad : public OpGrad { public: virtual std::vector onGrad(Express::EXPRP expr, const std::vector& backwardOutput) override { return {backwardOutput[0].sum({})}; } }; static void _create() { static ReduceGrad _c; OpGrad::insert(OpType_Reduction, &_c); static FillGrad _d; OpGrad::insert(OpType_Fill, &_d); } REGISTER_GRAD(ReduceGrad_cpp, _create); };