// // OpGrad.cpp // MNN // // Created by MNN on 2019/05/05. // Copyright © 2018, Alibaba Group Holding Limited // #include #include "OpGrad.hpp" using namespace std; using namespace MNN::Express; //#define MNN_TRAIN_DEBUG namespace MNN { extern void registerGradOps(); static std::map& getConverter() { static std::map gConverterMap; return gConverterMap; } OpGrad* OpGrad::get(int type) { auto& converterMap = getConverter(); auto iter = converterMap.find(type); if (iter != converterMap.end()) { return iter->second; } return nullptr; } void OpGrad::insert(int type, OpGrad* converter) { auto& converterMap = getConverter(); converterMap.insert(std::make_pair(type, converter)); } std::vector OpGrad::gradLinear(Express::VARP loss, const std::vector& parameters, const std::vector& outputDiff, const std::vector blockExpr) { std::map> backwardMap; auto outputSize = loss->expr().first->outputSize(); if (outputSize != outputDiff.size()) { MNN_ERROR("The expr output %d, but diff size is %d\n", outputSize, (int)outputDiff.size()); return {}; } backwardMap[loss->expr().first] = outputDiff; std::set parameterSet; for (auto p : parameters) { parameterSet.insert(p); } auto res = gradCommon({loss}, parameterSet, backwardMap, blockExpr); std::vector linearRes(parameters.size(), nullptr); for (int i=0; isecond; } } return linearRes; } std::map OpGrad::grad(VARP loss, const std::set& parameters, const std::vector blockName) { std::map> backwardMap; { auto shape = loss->getInfo(); MNN_ASSERT(shape->size == 1); auto init = _Const(1.0f, shape->dim, shape->order); backwardMap[loss->expr().first] = std::vector{init}; } return gradCommon({loss}, parameters, backwardMap, blockName); } Express::VARP OpGrad::divideAvoidZero(MNN::Express::VARP y, MNN::Express::VARP x) { auto p = MNN::Express::_Abs(x); auto sx = MNN::Express::_Sign(x); p = MNN::Express::_Maximum(p, MNN::Express::_Scalar(0.000001f)); return MNN::Express::_Divide(y, p) * sx; } static std::once_flag gInit; void OpGrad::init() { std::call_once(gInit, []() { registerGradOps(); }); } std::pair, std::vector> OpGrad::gradCommon(std::vector outputs, std::vector outputDiff, std::vector parameters) { if (outputs.size() != outputDiff.size()) { MNN_ERROR("outputDiff size %d not equal output size %d\n", (int)outputs.size(), (int)outputDiff.size()); return {}; } std::map> backwardMap; for (int i=0; iexpr(); if (backwardMap.find(expr.first) == backwardMap.end()) { std::vector res(expr.first->outputSize(), nullptr); backwardMap.insert(std::make_pair(expr.first, res)); } auto iter = backwardMap.find(expr.first); if (nullptr == iter->second[expr.second]) { iter->second[expr.second] = outputDiff[i]; } else { iter->second[expr.second] = iter->second[expr.second] + outputDiff[i]; } } std::set parameterSets; for (auto p : parameters) { parameterSets.insert(p); } auto varmap = gradCommon(outputs, parameterSets, backwardMap); std::vector res; std::vector resDiff; for (int i=0; ifirst); resDiff.push_back(iter->second); } } return std::make_pair(res, resDiff); } std::map OpGrad::gradCommon(std::vector outputs, const std::set& parameters, std::map>& backwardMap, const std::vector blockName) { init(); auto executeOrder = Variable::getExecuteOrder(outputs); for (auto iter = executeOrder.rbegin(); iter != executeOrder.rend(); iter++) { auto expr = *iter; auto& inputs = expr->inputs(); if (backwardMap.find(expr) == backwardMap.end()) { continue; } if (nullptr == expr->get()) { continue; } auto grad = OpGrad::get(expr->get()->type()); #ifdef MNN_TRAIN_DEBUG MNN_PRINT("Grad for %s, %s\n", expr->name().c_str(), MNN::EnumNameOpType(expr->get()->type())); #endif if (nullptr == grad) { #ifdef MNN_TRAIN_DEBUG MNN_PRINT("Can't grad for %s, %s\n", expr->name().c_str(), MNN::EnumNameOpType(expr->get()->type())); #endif continue; } auto inputGrad = grad->onGrad(expr, backwardMap[expr]); if (!expr->name().empty()) { for (int v=0; vsetName("grad::" + expr->name() + std::to_string(v)); } } } auto empty = true; for (auto grad : inputGrad) { if (nullptr != grad) { empty = false; break; } } if (empty) { #ifdef MNN_TRAIN_DEBUG MNN_PRINT("Can't grad for %s, %d\n", expr->name().c_str(), expr->get()->type()); #endif continue; } if (!blockName.empty()) { if (std::find(blockName.begin(), blockName.end(), expr->name()) != blockName.end()) { for (int ii = 0; ii getInfo(); if (nullptr == info) { MNN_ERROR("Grad error for %s, %d\n", expr->name().c_str(), expr->get()->type()); break; } } #endif MNN_ASSERT(inputGrad.size() <= inputs.size()); for (int i = 0; i < inputGrad.size(); ++i) { auto inputExpr = inputs[i]->expr().first; auto index = inputs[i]->expr().second; auto backward = inputGrad[i]; if (nullptr == backward) { continue; } if (backwardMap.find(inputExpr) == backwardMap.end()) { backwardMap.insert(std::make_pair(inputExpr, std::vector(inputExpr->outputSize()))); } auto& inputVarMap = backwardMap[inputExpr]; if (nullptr == inputVarMap[index]) { inputVarMap[index] = backward; } else { inputVarMap[index] = _Add(inputVarMap[index], backward); } } } std::map grads; std::map parametersExpr; for (auto p : parameters) { parametersExpr.insert(std::make_pair(p->expr().first.get(), p)); } for (auto iter : backwardMap) { auto expr = iter.first.get(); if (parametersExpr.find(expr) != parametersExpr.end()) { auto parameter = parametersExpr[expr]; grads[parameter] = iter.second[parameter->expr().second]; } } // MNN_PRINT("Grad: %d <- %d\n", grads.size(), parameters.size()); return grads; } } // namespace MNN