Files
2026-07-13 13:33:03 +08:00

339 lines
14 KiB
C++

//
// InterpGrad.cpp
// MNN
//
// Created by MNN on 2019/12/13.
// Copyright © 2018, Alibaba Group Holding Limited
//
#include "OpGrad.hpp"
#include "../source/geometry/ConvertUtils.hpp"
#include <vector>
#include <math.h>
using namespace std;
namespace MNN {
using namespace MNN::Express;
static int CLAMP(int v, int min, int max) {
if ((v) < min) {
(v) = min;
} else if ((v) > max) {
(v) = max;
}
return v;
}
// F = -0.75
static std::vector<float> CubicInterpolation2(float t) {
float b0 = 1.0f - 2.25f * t * t + 1.25f * t * t * t;
float c0 = 1.0f - 2.25f * (1.0f - t) * (1.0f - t) + 1.25f * (1.0f - t) * (1.0f - t) * (1.0f - t);
float t_a = 1.0f + t;
float t_d = 2.0f - t;
float a0 = 3.0f - 6.0f * (t_a) + 5.0f * 0.75 * t_a * t_a - 0.75f * t_a * t_a * t_a;
float d0 = 3.0f - 6.0f * (t_d) + 5.0f * 0.75 * t_d * t_d - 0.75f * t_d * t_d * t_d;
return {a0, b0, c0, d0};
}
// F = -0.75
static VARPS CubicInterpolation2(VARP t, VARPS &fs, int axis) {
auto sevenFive = fs[0];
auto one = fs[1];
auto oneTwoFive = fs[2];
auto two = fs[3];
auto twoTwoFive = fs[4];
auto three = fs[5];
auto five = fs[6];
auto six = fs[7];
auto st = _Square(t);
auto oneMinusT = one - t;
auto sOneMinusT = _Square(oneMinusT);
auto b0 = one - twoTwoFive * st + oneTwoFive * t * st;
auto c0 = one - twoTwoFive * sOneMinusT + oneTwoFive * oneMinusT * sOneMinusT;
auto ta = one + t;
auto sTa = _Square(ta);
auto td = two - t;
auto sTd = _Square(td);
auto a0 = three - six * ta + five * sevenFive * sTa - sevenFive * ta * sTa;
auto d0 = three - six * td + five * sevenFive * sTd - sevenFive * td * sTd;
return {a0, b0, c0, d0};
}
class InterpGrad : public OpGrad {
public:
virtual std::vector<Express::VARP> onGrad(Express::EXPRP expr,
const std::vector<Express::VARP>& backwardOutput) override {
auto op = expr->get();
auto originInterpParam = op->main_as_Interp();
auto inputs = expr->inputs();
auto inputInfo = expr->inputs()[0]->getInfo();
MNN_ASSERT(nullptr != inputInfo && inputInfo->dim.size() == 4);
auto outputDiff = backwardOutput[0];
outputDiff = _Convert(outputDiff, NCHW);
auto diffInfo = outputDiff->getInfo();
std::vector<VARP> res{nullptr};
const int inB = inputInfo->dim[0];
const int inC = inputInfo->dim[1];
const int inH = inputInfo->dim[2];
const int inW = inputInfo->dim[3];
const int outB = diffInfo->dim[0];
const int outC = diffInfo->dim[1];
const int outH = diffInfo->dim[2];
const int outW = diffInfo->dim[3];
int resizeType;
bool alignCorners;
InterpInfo info;
if (OpType_Resize == op->type()) {
alignCorners = false;
resizeType = 2; // Bilinear
info.widthScale = (float)inW / outW;
info.heightScale = (float)inH / outH;
} else {
MNN_ASSERT(OpType_Interp == op->type());
resizeType = originInterpParam->resizeType();
alignCorners = originInterpParam->alignCorners();
bool computeScale = true;
if (inputs.size() > 1 && inputs[1]->getInfo()->type.code == halide_type_float) {
computeScale = false;
auto scalePtr = inputs[1]->readMap<float>();
info.heightScale = 1.0f / scalePtr[2];
if (inputs[0]->getInfo()->dim.size() >= 4) {
info.widthScale = 1.0f / scalePtr[3];
}
}
MNN_ASSERT(nullptr != inputInfo && inputInfo->dim.size() == 4);
const int defaultDepth = 10;
_ConverterInterp(originInterpParam, &info, inW, inH, defaultDepth, outW, outH, defaultDepth, computeScale);
}
// nearest, nearest_round
if (resizeType == 1 || resizeType == 4) {
const float scaleH = info.heightScale;
const float scaleW = info.widthScale;
vector<int> vecH, vecW;
for (int i = 0; i < outH; i++) {
vecH.push_back(i);
}
for (int i = 0; i < outW; i++) {
vecW.push_back(i);
}
auto varH = _Const((void*)vecH.data(), {outH, 1, 1}, NCHW, halide_type_of<int>());
auto varW = _Const((void*)vecW.data(), {1, outW, 1}, NCHW, halide_type_of<int>());
if (resizeType == 1) {
varH = _Cast<int>(_Floor(_Scalar<float>(scaleH) * _Cast<float>(varH)));
varW = _Cast<int>(_Floor(_Scalar<float>(scaleW) * _Cast<float>(varW)));
} else {
varH = _Cast<int>(_Round(_Scalar<float>(scaleH) * (_Cast<float>(varH) + _Scalar<float>(0.5f)) - _Scalar<float>(0.5f)));
varW = _Cast<int>(_Round(_Scalar<float>(scaleW) * (_Cast<float>(varW) + _Scalar<float>(0.5f)) - _Scalar<float>(0.5f)));
}
varH = Express::_Maximum(varH, _Scalar<int>(0));
varH = Express::_Minimum(varH, _Scalar<int>(inH - 1));
varW = Express::_Maximum(varW, _Scalar<int>(0));
varW = Express::_Minimum(varW, _Scalar<int>(inW - 1));
auto expandH = varH * _Cast<int>(_Const(1.0f, {1, outW, 1}));
auto expandW = varW * _Cast<int>(_Const(1.0f, {outH, 1, 1}));
auto indices = _Concat({expandH, expandW}, -1);
auto updates = _Transpose(outputDiff, {2, 3, 0, 1}); // HWNC
std::vector<int> inputShape = {inH, inW, inB, inC};
auto shape = _Const(inputShape.data(), {4}, NCHW, halide_type_of<int>());
auto temp = _ScatterNd(indices, updates, shape, 0); // 0 for add
res[0] = _Transpose(temp, {2, 3, 0, 1}); // NCHW
}
if (resizeType == 2) {
const float scaleH = info.heightScale;
const float scaleW = info.widthScale;
const float offsetH = info.heightOffset;
const float offsetW = info.widthOffset;
vector<int> vecH, vecW;
for (int i = 0; i < outH; i++) {
vecH.push_back(i);
}
for (int i = 0; i < outW; i++) {
vecW.push_back(i);
}
auto varH = _Const((void*)vecH.data(), {outH, 1, 1}, NCHW, halide_type_of<int>());
auto varW = _Const((void*)vecW.data(), {1, outW, 1}, NCHW, halide_type_of<int>());
// shape: outH * 1 * 1
auto realH = _Cast<float>(varH) * _Scalar<float>(scaleH) + _Scalar<float>(offsetH);
auto topH = _Cast<int>(_Floor(realH));
auto h0 = Express::_Maximum(topH, _Scalar<int>(0));
h0 = Express::_Minimum(h0, _Scalar<int>(inH-1));
auto h1 = Express::_Maximum(topH+_Scalar<int>(1), _Scalar<int>(0));
h1 = Express::_Minimum(h1, _Scalar<int>(inH-1));
auto factorH = realH - _Cast<float>(topH);
// shape: 1 * outW * 1
auto realW = _Cast<float>(varW) * _Scalar<float>(scaleW) + _Scalar<float>(offsetW);
auto leftW = _Cast<int>(_Floor(realW));
auto w0 = Express::_Maximum(leftW, _Scalar<int>(0));
w0 = Express::_Minimum(w0, _Scalar<int>(inW-1));
auto w1 = Express::_Maximum(leftW+_Scalar<int>(1), _Scalar<int>(0));
w1 = Express::_Minimum(w1, _Scalar<int>(inW-1));
auto factorW = realW - _Cast<float>(leftW);
auto one = _Scalar<float>(1.0f);
// shape: outH * outW * 1
auto f0 = (one - factorH) * (one - factorW);
auto f1 = (one - factorH) * factorW;
auto f2 = factorH * (one - factorW);
auto f3 = factorH * factorW;
// shape: outH * outW * 1
auto expandH0 = h0 * _Cast<int>(_Const(1.0f, {1, outW, 1}));
auto expandH1 = h1 * _Cast<int>(_Const(1.0f, {1, outW, 1}));
auto expandW0 = w0 * _Cast<int>(_Const(1.0f, {outH, 1, 1}));
auto expandW1 = w1 * _Cast<int>(_Const(1.0f, {outH, 1, 1}));
// shape: outH * outW * 2
auto h0w0 = _Concat({expandH0, expandW0}, -1);
auto h0w1 = _Concat({expandH0, expandW1}, -1);
auto h1w0 = _Concat({expandH1, expandW0}, -1);
auto h1w1 = _Concat({expandH1, expandW1}, -1);
auto updates = _Transpose(outputDiff, {2, 3, 0, 1}); // HWNC
vector<int> factorShape = {outH, outW, 1, 1};
auto u00 = _Reshape(f0, factorShape) * updates;
auto u01 = _Reshape(f1, factorShape) * updates;
auto u10 = _Reshape(f2, factorShape) * updates;
auto u11 = _Reshape(f3, factorShape) * updates;
std::vector<int> inputShape = {inH, inW, inB, inC};
auto shape = _Const(inputShape.data(), {4}, NCHW, halide_type_of<int>());
auto temp0 = _ScatterNd(h0w0, u00, shape, 0); // 0 for add
auto temp1 = _ScatterNd(h0w1, u01, shape, 0);
auto temp2 = _ScatterNd(h1w0, u10, shape, 0);
auto temp3 = _ScatterNd(h1w1, u11, shape, 0);
auto temp = temp0 + temp1 + temp2 + temp3;
res[0] = _Transpose(temp, {2, 3, 0, 1}); // NCHW
}
if (resizeType == 3) {
const float scaleH = info.heightScale;
const float scaleW = info.widthScale;
const float offsetH = info.heightOffset;
const float offsetW = info.widthOffset;
vector<int> vecH, vecW;
for (int i = 0; i < outH; i++) {
vecH.push_back(i);
}
for (int i = 0; i < outW; i++) {
vecW.push_back(i);
}
auto varH = _Const((void*)vecH.data(), {outH, 1, 1}, NCHW, halide_type_of<int>());
auto varW = _Const((void*)vecW.data(), {1, outW, 1}, NCHW, halide_type_of<int>());
// shape: outH * 1 * 1
auto realH = _Cast<float>(varH) * _Scalar<float>(scaleH) + _Scalar<float>(offsetH);
auto topH = _Cast<int>(realH);
auto h0 = Express::_Maximum(topH-_Scalar<int>(1), _Scalar<int>(0));
h0 = Express::_Minimum(h0, _Scalar<int>(inH-1));
auto h1 = Express::_Maximum(topH, _Scalar<int>(0));
h1 = Express::_Minimum(h1, _Scalar<int>(inH-1));
auto h2 = Express::_Maximum(topH+_Scalar<int>(1), _Scalar<int>(0));
h2 = Express::_Minimum(h2, _Scalar<int>(inH-1));
auto h3 = Express::_Maximum(topH+_Scalar<int>(2), _Scalar<int>(0));
h3 = Express::_Minimum(h3, _Scalar<int>(inH-1));
auto factorH = realH - _Floor(_Cast<float>(realH));
// shape: 1 * outW * 1
auto realW = _Cast<float>(varW) * _Scalar<float>(scaleW) + _Scalar<float>(offsetW);
auto leftW = _Cast<int>(realW);
auto w0 = Express::_Maximum(leftW-_Scalar<int>(1), _Scalar<int>(0));
w0 = Express::_Minimum(w0, _Scalar<int>(inW-1));
auto w1 = Express::_Maximum(leftW, _Scalar<int>(0));
w1 = Express::_Minimum(w1, _Scalar<int>(inW-1));
auto w2 = Express::_Maximum(leftW+_Scalar<int>(1), _Scalar<int>(0));
w2 = Express::_Minimum(w2, _Scalar<int>(inW-1));
auto w3 = Express::_Maximum(leftW+_Scalar<int>(2), _Scalar<int>(0));
w3 = Express::_Minimum(w3, _Scalar<int>(inW-1));
auto factorW = realW - _Floor(_Cast<float>(realW));
auto sevenFive = _Scalar<float>(0.75f);
auto one = _Scalar<float>(1.0f);
auto oneTwoFive = _Scalar<float>(1.25f);
auto two = _Scalar<float>(2.0f);
auto twoTwoFive = _Scalar<float>(2.25f);
auto three = _Scalar<float>(3.0f);
auto five = _Scalar<float>(5.0f);
auto six = _Scalar<float>(6.0f);
VARPS fs = {sevenFive, one, oneTwoFive, two, twoTwoFive, three, five, six};
// shape: outH * 1 * 4 * 1
auto hFactors = CubicInterpolation2(_Reshape(factorH, {outH, 1, 1, 1}), fs, 2);
// shape: 1 * outW * 1 * 4
auto wFactors = CubicInterpolation2(_Reshape(factorW, {1, outW, 1, 1}), fs, 3);
// shape: outH * outW * 1
auto expandH0 = h0 * _Cast<int>(_Const(1.0f, {1, outW, 1}));
auto expandH1 = h1 * _Cast<int>(_Const(1.0f, {1, outW, 1}));
auto expandH2 = h2 * _Cast<int>(_Const(1.0f, {1, outW, 1}));
auto expandH3 = h3 * _Cast<int>(_Const(1.0f, {1, outW, 1}));
auto expandW0 = w0 * _Cast<int>(_Const(1.0f, {outH, 1, 1}));
auto expandW1 = w1 * _Cast<int>(_Const(1.0f, {outH, 1, 1}));
auto expandW2 = w2 * _Cast<int>(_Const(1.0f, {outH, 1, 1}));
auto expandW3 = w3 * _Cast<int>(_Const(1.0f, {outH, 1, 1}));
VARPS hIndices = {expandH0, expandH1, expandH2, expandH3};
VARPS wIndices = {expandW0, expandW1, expandW2, expandW3};
auto updates = _Transpose(outputDiff, {2, 3, 0, 1}); // HWNC
std::vector<int> inputShape = {inH, inW, inB, inC};
auto shape = _Const(inputShape.data(), {4}, NCHW, halide_type_of<int>());
// shape: outH * outW * 2
VARPS hwIndices;
// shape: outH * outW * outB * outC
VARPS hwUpdates;
VARP tempRes = _Const(0.0f, inputShape, NCHW);
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
// shape: outH * outW * 2
auto hwIndices = _Concat({hIndices[i], wIndices[j]}, -1);
// shape: outH * outW * outB * outC
auto hwUpdates = hFactors[i] * wFactors[j] * updates;
auto temp = _ScatterNd(hwIndices, hwUpdates, shape, 0); // 0 for add
tempRes = tempRes + temp;
}
}
res[0] = _Transpose(tempRes, {2, 3, 0, 1}); // NCHW
}
res[0] = _Convert(res[0], inputInfo->order);
return res;
}
};
static void _create() {
static InterpGrad _c;
OpGrad::insert((int)OpType_Interp, &_c);
OpGrad::insert((int)OpType_Resize, &_c);
}
REGISTER_GRAD(InterpGrad_cpp, _create);
};