// // LoopGrad.cpp // MNN // // Created by MNN on b'2022/10/10'. // Copyright © 2018, Alibaba Group Holding Limited // #include "OpGrad.hpp" using namespace std; namespace MNN { using namespace MNN::Express; class LoopGrad : public OpGrad { public: static std::vector _getGradExpr(EXPRP expr, std::vector inputs, VARP tempOutput) { auto gradValue = OpGrad::get(expr->get()->type())->onGrad(expr, {tempOutput}); MNN_ASSERT(gradValue.size() == inputs.size()); return gradValue; } static void _gradForCommandMatMul(const RegionCommandT* command, const std::map& backwardMap, int tensorNumber, std::map& extraInputs, std::map& extraOutputs, std::vector>& dstCommands) { auto AIndex = command->indexes[1]; auto BIndex = command->indexes[2]; auto CIndex = command->indexes[0]; auto transA = command->op->main.AsMatMul()->transposeA; auto transB = command->op->main.AsMatMul()->transposeB; int e = command->size[0]; int l = command->size[1]; int h = command->size[2]; if (backwardMap.find(CIndex) == backwardMap.end()) { return; } std::map originIndexMap; for (int i=0; iindexes.size(); ++i) { auto index = command->indexes[i]; originIndexMap.insert(std::make_pair(index, i)); auto bkIter = backwardMap.find(index); if (bkIter != backwardMap.end()) { originIndexMap.insert(std::make_pair(bkIter->second, i)); } } auto CDiffIndex = backwardMap.find(CIndex)->second; if (backwardMap.find(AIndex) != backwardMap.end()) { auto ADiffIndex = backwardMap.find(AIndex)->second; // Compute A Diff std::unique_ptr currentCommand(new RegionCommandT); currentCommand->op.reset(new OpT); currentCommand->op->type = OpType_MatMul; currentCommand->op->main.value = new MatMulT; currentCommand->op->main.type = OpParameter_MatMul; currentCommand->indexes = {ADiffIndex, CDiffIndex, BIndex}; currentCommand->view.resize(3); for (int j=0; jview.size(); ++j) { currentCommand->view[j].reset(new ViewT); } currentCommand->iterIndexes.resize(3); currentCommand->steps.resize(3); for (int j=0; jindexes.size(); ++j) { // Compute output info auto index = currentCommand->indexes[j]; currentCommand->iterIndexes[j] = command->iterIndexes[originIndexMap[index]]; currentCommand->steps[j] = command->steps[originIndexMap[index]]; *currentCommand->view[j] = *command->view[originIndexMap[index]]; } // TODO: Optimize fuse option currentCommand->fuse = BinaryOpOperation_ADD; // Reorder the view's stride by size order change to e, h, l std::vector order = {0, 2, 1}; currentCommand->size = {e, h, l}; for (int j=0; jindexes.size(); ++j) { auto view = currentCommand->view[j].get(); auto originStride = view->stride; for (int k=0; k<3; ++k) { view->stride[k] = originStride[order[k]]; } } // Set Transpose Info by stride auto dstMatMulParam = currentCommand->op->main.AsMatMul(); { auto transAView = currentCommand->view[1].get(); auto transBView = currentCommand->view[1].get(); dstMatMulParam->transposeA = transAView->stride[1] != 1; dstMatMulParam->transposeB = transBView->stride[1] == 1; } dstCommands.emplace_back(std::move(currentCommand)); } if (backwardMap.find(BIndex) != backwardMap.end()) { auto BDiffIndex = backwardMap.find(BIndex)->second; // Compute A Diff std::unique_ptr currentCommand(new RegionCommandT); currentCommand->op.reset(new OpT); currentCommand->op->type = OpType_MatMul; currentCommand->op->main.value = new MatMulT; currentCommand->op->main.type = OpParameter_MatMul; currentCommand->indexes = {BDiffIndex, CDiffIndex, AIndex}; currentCommand->view.resize(3); for (int j=0; jview.size(); ++j) { currentCommand->view[j].reset(new ViewT); } currentCommand->iterIndexes.resize(3); currentCommand->steps.resize(3); for (int j=0; jindexes.size(); ++j) { // Compute output info auto index = currentCommand->indexes[j]; currentCommand->iterIndexes[j] = command->iterIndexes[originIndexMap[index]]; currentCommand->steps[j] = command->steps[originIndexMap[index]]; *currentCommand->view[j] = *command->view[originIndexMap[index]]; } // TODO: Optimize fuse option currentCommand->fuse = BinaryOpOperation_ADD; // Reorder the view's stride by size order change to e, h, l std::vector order = {2, 0, 1}; currentCommand->size = {h, e, l}; for (int j=0; jindexes.size(); ++j) { auto view = currentCommand->view[j].get(); auto originStride = view->stride; for (int k=0; k<3; ++k) { view->stride[k] = originStride[order[k]]; } } // Set Transpose Info by stride auto dstMatMulParam = currentCommand->op->main.AsMatMul(); { auto transAView = currentCommand->view[1].get(); auto transBView = currentCommand->view[1].get(); dstMatMulParam->transposeA = transAView->stride[1] != 1; dstMatMulParam->transposeB = transBView->stride[1] == 1; } dstCommands.emplace_back(std::move(currentCommand)); } } static void _gradForCommand(const RegionCommandT* command, const std::map& backwardMap, int tensorNumber, std::map& extraInputs, std::map& extraOutputs, std::vector>& dstCommands) { if (command->op->type == OpType_MatMul) { _gradForCommandMatMul(command, backwardMap, tensorNumber, extraInputs, extraOutputs, dstCommands); return; } if (command->op->type == OpType_UnaryOp) { auto inputIndex = command->indexes[1]; auto outputIndex = command->indexes[0]; if (backwardMap.find(inputIndex) == backwardMap.end()) { return; } MNN_ASSERT(backwardMap.find(outputIndex) != backwardMap.end()); auto bpInput = backwardMap.find(outputIndex)->second; auto bpOutput = backwardMap.find(inputIndex)->second; if (nullptr == command->op->main.value) { std::unique_ptr currentCommand(new RegionCommandT); currentCommand->op.reset(new OpT); currentCommand->op->type = OpType_UnaryOp; currentCommand->fuse = BinaryOpOperation_ADD; currentCommand->op->main.type = OpParameter_NONE; currentCommand->indexes = {bpOutput, bpInput}; currentCommand->view.resize(2); currentCommand->view[0].reset(new ViewT); currentCommand->view[1].reset(new ViewT); *currentCommand->view[0] = *command->view[1]; *currentCommand->view[1] = *command->view[0]; currentCommand->size = command->size; currentCommand->iterIndexes = {command->iterIndexes[1], command->iterIndexes[0]}; currentCommand->steps = {command->steps[1], command->steps[0]}; dstCommands.emplace_back(std::move(currentCommand)); return; } FUNC_PRINT(1); } int inputSize = 0; std::vector inputs; if (command->op->type == OpType_BinaryOp) { inputSize = 2; } else if (command->op->type == OpType_UnaryOp) { inputSize = 1; } else { MNN_ASSERT(false); // TODO: Support MatMul } for (int i=0; i, int> allTensors; auto commandExpr = Expr::create(command->op.get(), inputs, 1); allTensors.insert(std::make_pair(std::make_pair(commandExpr, 0), command->indexes[0])); for (int i=0; iexpr(), command->indexes[i+1])); } std::map originIndexMap; for (int i=0; iindexes.size(); ++i) { auto index = command->indexes[i]; originIndexMap.insert(std::make_pair(index, i)); auto bkIter = backwardMap.find(index); if (bkIter != backwardMap.end()) { originIndexMap.insert(std::make_pair(bkIter->second, i)); } } auto gradOutputIter = backwardMap.find(command->indexes[0]); MNN_ASSERT(gradOutputIter != backwardMap.end()); allTensors.insert(std::make_pair(tempOutput->expr(), gradOutputIter->second)); auto backwardVars = _getGradExpr(commandExpr, inputs, tempOutput); for (int i=0; iindexes[1 + i]); if (gradInputIter != backwardMap.end()) { if (backwardVars[i]->expr().first->get() == nullptr) { // Make Copy Command auto inputIndexIter = allTensors.find(backwardVars[i]->expr()); MNN_ASSERT(inputIndexIter != allTensors.end()); std::unique_ptr currentCommand(new RegionCommandT); currentCommand->op.reset(new OpT); currentCommand->op->type = OpType_UnaryOp; currentCommand->indexes.resize(2); currentCommand->indexes[0] = gradInputIter->second; currentCommand->indexes[1] = inputIndexIter->second; currentCommand->view.resize(2); for (int j=0; jview.size(); ++j) { currentCommand->view[j].reset(new ViewT); } currentCommand->iterIndexes.resize(2); currentCommand->steps.resize(2); // Compute output info currentCommand->iterIndexes[0] = command->iterIndexes[originIndexMap[currentCommand->indexes[0]]]; currentCommand->steps[0] = command->steps[originIndexMap[currentCommand->indexes[0]]]; *currentCommand->view[0] = *command->view[originIndexMap[currentCommand->indexes[0]]]; currentCommand->size = command->size; // TODO: Optimize fuse option currentCommand->fuse = BinaryOpOperation_ADD; *currentCommand->view[1] = *command->view[originIndexMap[currentCommand->indexes[1]]]; currentCommand->iterIndexes[1] = command->iterIndexes[originIndexMap[currentCommand->indexes[1]]]; currentCommand->steps[1] = command->steps[originIndexMap[currentCommand->indexes[1]]]; dstCommands.emplace_back(std::move(currentCommand)); } else { allTensors.insert(std::make_pair(backwardVars[i]->expr(), gradInputIter->second)); } } } } auto exprLists = Variable::getExecuteOrder(backwardVars); for (int i=0; ioutputSize()); auto iter = allTensors.find(std::make_pair(curExpr, 0)); MNN_ASSERT(iter != allTensors.end()); if (nullptr == curExpr->get()) { continue; } // Make Command std::unique_ptr currentCommand(new RegionCommandT); currentCommand->op.reset(curExpr->get()->UnPack()); currentCommand->indexes.resize(curExpr->inputs().size() + curExpr->outputSize()); currentCommand->indexes[0] = iter->second; currentCommand->view.resize(curExpr->inputs().size() + curExpr->outputSize()); for (int j=0; jview.size(); ++j) { currentCommand->view[j].reset(new ViewT); } currentCommand->iterIndexes.resize(curExpr->inputs().size() + curExpr->outputSize()); currentCommand->steps.resize(curExpr->inputs().size() + curExpr->outputSize()); // Compute output info currentCommand->iterIndexes[0] = command->iterIndexes[originIndexMap[iter->second]]; currentCommand->steps[0] = command->steps[originIndexMap[iter->second]]; *currentCommand->view[0] = *command->view[originIndexMap[iter->second]]; currentCommand->size = command->size; // TODO: Optimize fuse option currentCommand->fuse = BinaryOpOperation_ADD; for (int j=0; jinputs().size(); ++j) { iter = allTensors.find(curExpr->inputs()[j]->expr()); if (iter == allTensors.end()) { MNN_ASSERT(false); } currentCommand->indexes[1 + j] = iter->second; MNN_ASSERT(originIndexMap.find(iter->second) != originIndexMap.end()); *currentCommand->view[1 + j] = *command->view[originIndexMap[iter->second]]; currentCommand->iterIndexes[1 + j] = command->iterIndexes[originIndexMap[iter->second]]; currentCommand->steps[1 + j] = command->steps[originIndexMap[iter->second]]; } dstCommands.emplace_back(std::move(currentCommand)); } MNN_ASSERT(dstCommands.size() > 0); } virtual std::vector onGrad(Express::EXPRP expr, const std::vector& backwardOutput) override { auto inputs = expr->inputs(); std::vector result(inputs.size(), nullptr); auto op = expr->get(); if (op->main_type() != OpParameter_LoopParam) { return result; } std::unique_ptr srcParam(op->main_as_LoopParam()->UnPack()); MNN_ASSERT(srcParam->inputIndexes.size() == inputs.size()); MNN_ASSERT(srcParam->outputIndexes.size() == backwardOutput.size()); std::unique_ptr dstParam(new LoopParamT); dstParam->loopNumber = srcParam->loopNumber; dstParam->inputIndexes.resize(srcParam->inputIndexes.size() + srcParam->outputIndexes.size() + backwardOutput.size()); int dstParamTensorSrcInputOffset = 0; int dstParamTensorSrcOutputOffset = srcParam->inputIndexes.size(); int dstParamTensorDstOutputOffset = srcParam->tensorNumber; int dstParamTensorDstInputOffset = srcParam->tensorNumber + backwardOutput.size(); ::memcpy(dstParam->inputIndexes.data() + 0, srcParam->inputIndexes.data(), srcParam->inputIndexes.size() * sizeof(int)); ::memcpy(dstParam->inputIndexes.data() + srcParam->inputIndexes.size(), srcParam->outputIndexes.data(), srcParam->outputIndexes.size() * sizeof(int)); std::map backwardMap; for (int i=0; iinputIndexes[srcParam->inputIndexes.size() + srcParam->outputIndexes.size() + i] = dstParamTensorDstOutputOffset + i; backwardMap.insert(std::make_pair(srcParam->outputIndexes[i], dstParamTensorDstOutputOffset + i)); } dstParam->tensorNumber = srcParam->tensorNumber + backwardOutput.size(); dstParam->outputIndexes.clear(); std::vector gradInputs; for (int i=0; igetInfo()->type.code != halide_type_float) { continue; } gradInputs.emplace_back(i); auto curNumber = dstParam->tensorNumber; dstParam->outputIndexes.emplace_back(curNumber); dstParam->tensorNumber++; backwardMap.insert(std::make_pair(srcParam->inputIndexes[i], curNumber)); } // Clear zero firstly for backward inputs for (auto index : srcParam->inputIndexes) { auto iter = backwardMap.find(index); if (iter == backwardMap.end()) { continue; } std::unique_ptr zeroCmd(new RegionCommandT); zeroCmd->indexes = {iter->second}; dstParam->initCommand.emplace_back(std::move(zeroCmd)); } std::map extraVarps; std::map extraOutputVarps; for (int i=0; icommands.size(); ++i) { auto reverseI = (int)srcParam->commands.size() - 1 - i; auto cmd = srcParam->commands[reverseI].get(); _gradForCommand(cmd, backwardMap, dstParam->tensorNumber, extraVarps, extraOutputVarps, dstParam->commands); } // Make Op std::vector loopInputs(inputs.size() + 2 * expr->outputSize() + extraVarps.size()); for (int i=0; ioutputSize(); ++i) { loopInputs[i + inputs.size()] = Variable::create(expr, i); } for (int i=0; ioutputSize(); ++i) { loopInputs[i + inputs.size() + expr->outputSize()] = backwardOutput[i]; } for (auto& iter : extraVarps) { loopInputs[inputs.size() + 2 * expr->outputSize() + iter.first] = iter.second; } MNN_ASSERT(dstParam->commands.size() > 0); MNN_ASSERT(dstParam->outputIndexes.size() == gradInputs.size()); for (int i=0; igetInfo(); std::unique_ptr describe(new TensorDescribeT); describe->index = dstParam->outputIndexes[i]; describe->blob.reset(new BlobT); describe->blob->dims = info->dim; describe->blob->dataType = DataType_DT_FLOAT; switch (info->order) { case MNN::Express::NCHW: describe->blob->dataFormat = MNN_DATA_FORMAT_NCHW; break; case MNN::Express::NHWC: describe->blob->dataFormat = MNN_DATA_FORMAT_NHWC; break; case MNN::Express::NC4HW4: describe->blob->dataFormat = MNN_DATA_FORMAT_NC4HW4; break; default: break; } dstParam->extraTensorInfos.emplace_back(std::move(describe)); } std::unique_ptr loopOp(new OpT); loopOp->type = OpType_While; loopOp->main.type = OpParameter_LoopParam; loopOp->main.value = dstParam.release(); auto gradExpr = Expr::create(std::move(loopOp), loopInputs, gradInputs.size()); for (int i=0; i