chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,345 @@
|
||||
//
|
||||
// TFConvolutionMerge.cpp
|
||||
// MNNConverter
|
||||
//
|
||||
// Created by MNN on 2019/09/16.
|
||||
// Copyright © 2018, Alibaba Group Holding Limited
|
||||
//
|
||||
|
||||
#include <algorithm>
|
||||
#include "MNN_generated.h"
|
||||
#include "TFExtraManager.hpp"
|
||||
#include "core/OpCommonUtils.hpp"
|
||||
|
||||
namespace MNN {
|
||||
namespace Express {
|
||||
static bool _writeCommonAttr(Convolution2DCommonT* common, const Extra* extra, const std::string& name) {
|
||||
if (nullptr == extra || nullptr == extra->attr()) {
|
||||
return false;
|
||||
}
|
||||
auto attrSize = extra->attr()->size();
|
||||
for (int v = 0; v < attrSize; ++v) {
|
||||
auto attr = extra->attr()->GetAs<Attribute>(v);
|
||||
const auto key = attr->key()->str();
|
||||
auto list = attr->list();
|
||||
// "rates" for tf.nn.atrous_conv2d
|
||||
// "dilations" for tf.nn.conv2d or tf.nn.dilation2d or tf.nn.conv2d_transpose
|
||||
// "rate" has been here when I change the code, so I reserve it though I don't know where use it
|
||||
if (key == "rate" || key == "rates" || key == "dilations") {
|
||||
common->dilateX = list->i()->data()[2];
|
||||
common->dilateY = list->i()->data()[1];
|
||||
} else if (key == "strides") {
|
||||
common->strideX = list->i()->data()[2];
|
||||
common->strideY = list->i()->data()[1];
|
||||
} else if (key == "padding") {
|
||||
common->padMode = MNN::PadMode_SAME;
|
||||
auto paddingType = attr->s()->str();
|
||||
if (paddingType == "VALID") {
|
||||
common->padMode = MNN::PadMode_VALID;
|
||||
} else if (paddingType == "Symmetric") {
|
||||
common->padMode = MNN::PadMode_CAFFE;
|
||||
common->padX = 1;
|
||||
common->padY = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
class ConvolutionTransform : public TFExtraManager::Transform {
|
||||
public:
|
||||
virtual EXPRP onExecute(EXPRP expr) const override {
|
||||
auto op = expr->get();
|
||||
auto inputs = expr->inputs();
|
||||
auto weight = inputs[1];
|
||||
auto weightInfo = weight->getInfo();
|
||||
auto weightTensorData = weight->readMap<float>();
|
||||
std::unique_ptr<Convolution2DT> convolution2D(new MNN::Convolution2DT);
|
||||
convolution2D->common.reset(new MNN::Convolution2DCommonT);
|
||||
auto common = convolution2D->common.get();
|
||||
common->relu = false;
|
||||
common->group = 1;
|
||||
common->padX = 0;
|
||||
common->padY = 0;
|
||||
common->outputCount = 0;
|
||||
|
||||
bool success = _writeCommonAttr(common, op->main_as_Extra(), op->name()->str());
|
||||
if (!success) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!weightInfo || !weightTensorData) {
|
||||
std::unique_ptr<OpT> newOp(new OpT);
|
||||
newOp->name = expr->name();
|
||||
newOp->type = OpType_Convolution;
|
||||
newOp->main.type = OpParameter_Convolution2D;
|
||||
newOp->main.value = convolution2D.release();
|
||||
// Turn weight to NCHW
|
||||
inputs[1] = _Transpose(inputs[1], {3, 2, 0, 1});
|
||||
auto newExpr = Expr::create(newOp.get(), inputs, 1);
|
||||
return newExpr;
|
||||
}
|
||||
int kh = weightInfo->dim[0];
|
||||
int kw = weightInfo->dim[1];
|
||||
int num_input = weightInfo->dim[2];
|
||||
int weight_input = weightInfo->dim[2];
|
||||
common->kernelX = kw;
|
||||
common->kernelY = kh;
|
||||
auto src = inputs[0];
|
||||
auto srcInfo = src->getInfo();
|
||||
if (nullptr != srcInfo && srcInfo->dim.size() > 0) {
|
||||
if (NHWC == srcInfo->order) {
|
||||
num_input = srcInfo->dim[(int)srcInfo->dim.size() - 1];
|
||||
} else {
|
||||
num_input = srcInfo->dim[1];
|
||||
}
|
||||
}
|
||||
int num_output = weightInfo->dim[3];
|
||||
common->outputCount = num_output;
|
||||
common->inputCount = num_input;
|
||||
if (0 != weight_input) {
|
||||
common->group = num_input / weight_input;
|
||||
}
|
||||
if (common->group < 1) {
|
||||
common->group = 1;
|
||||
}
|
||||
weight = _Transpose(weight, {3, 2, 0, 1});
|
||||
weightInfo = weight->getInfo();
|
||||
weightTensorData = weight->readMap<float>();
|
||||
|
||||
convolution2D->bias.resize(num_output);
|
||||
std::fill(convolution2D->bias.begin(), convolution2D->bias.end(), 0.0f);
|
||||
convolution2D->weight.resize(weightInfo->size);
|
||||
::memcpy(convolution2D->weight.data(), weightTensorData, weightInfo->size * sizeof(float));
|
||||
std::unique_ptr<OpT> newOp(new OpT);
|
||||
newOp->name = expr->name();
|
||||
newOp->type = OpType_Convolution;
|
||||
newOp->main.type = OpParameter_Convolution2D;
|
||||
newOp->main.value = convolution2D.release();
|
||||
auto newExpr = Expr::create(newOp.get(), {inputs[0]}, 1);
|
||||
return newExpr;
|
||||
}
|
||||
};
|
||||
|
||||
class ConvolutionDepthwiseTransform : public TFExtraManager::Transform {
|
||||
public:
|
||||
virtual EXPRP onExecute(EXPRP expr) const override {
|
||||
auto op = expr->get();
|
||||
auto inputs = expr->inputs();
|
||||
auto input = inputs[0];
|
||||
auto weight = inputs[1];
|
||||
auto weightInfo = weight->getInfo();
|
||||
auto weightTensorData = weight->readMap<float>();
|
||||
if (!weightInfo || !weightTensorData) {
|
||||
MNN_ERROR("For %s convolution weight is not const\n", expr->name().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<Convolution2DT> convolution2D(new MNN::Convolution2DT);
|
||||
int kh = weightInfo->dim[0];
|
||||
int kw = weightInfo->dim[1];
|
||||
int num_input = weightInfo->dim[2];
|
||||
int multiplier = weightInfo->dim[3];
|
||||
int num_output = num_input * multiplier;
|
||||
weight = _Transpose(weight, {3, 2, 0, 1});
|
||||
if (multiplier <= 1) {
|
||||
weightInfo = weight->getInfo();
|
||||
weightTensorData = weight->readMap<float>();
|
||||
int once_weight = weightInfo->size / multiplier;
|
||||
convolution2D->weight.resize(once_weight);
|
||||
::memcpy(convolution2D->weight.data(), weightTensorData, weightInfo->size * sizeof(float));
|
||||
convolution2D->bias.resize(num_output);
|
||||
std::fill(convolution2D->bias.begin(), convolution2D->bias.end(), 0.0f);
|
||||
}
|
||||
convolution2D->common.reset(new MNN::Convolution2DCommonT);
|
||||
auto common = convolution2D->common.get();
|
||||
|
||||
common->relu = false;
|
||||
common->group = num_input;
|
||||
common->outputCount = num_input;
|
||||
common->inputCount = num_input;
|
||||
common->kernelX = kw;
|
||||
common->kernelY = kh;
|
||||
common->padX = 0;
|
||||
common->padY = 0;
|
||||
|
||||
bool success = _writeCommonAttr(common, op->main_as_Extra(), op->name()->str());
|
||||
if (!success) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<OpT> newOp(new OpT);
|
||||
newOp->name = expr->name();
|
||||
newOp->type = OpType_ConvolutionDepthwise;
|
||||
newOp->main.type = OpParameter_Convolution2D;
|
||||
newOp->main.value = convolution2D.release();
|
||||
if (multiplier <= 1) {
|
||||
return (Expr::create(newOp.get(), {inputs[0]}, 1));
|
||||
}
|
||||
std::vector<int> split(multiplier, 1);
|
||||
auto weights = _Split(weight, split);
|
||||
std::vector<VARP> convs(multiplier);
|
||||
for (int i = 0; i < multiplier; i++) {
|
||||
convs[i] = (Variable::create(Expr::create(newOp.get(), {inputs[0], weights[i]})));
|
||||
}
|
||||
// NHWC => NMHWC (Raster: NCHW => NMCHW)
|
||||
auto x = _Concat(convs, 1);
|
||||
// NMHWC => NMAC (Raster: NMCHW => NMCA)
|
||||
auto shape = _Split(_Shape(convs[0]), {1, 1, 1, 1}, 0);
|
||||
auto batch_n = shape[0];
|
||||
auto kernel_h = shape[1];
|
||||
auto kernel_w = shape[2];
|
||||
auto input_c = shape[3];
|
||||
auto multip = _Const(&multiplier, {1}, NHWC, halide_type_of<int>());
|
||||
x = _Reshape(x, _Concat({batch_n, multip, _Multiply(kernel_h, kernel_w), input_c}, 0));
|
||||
// NMAC => NACM (Raster: NMCA => NCMA)
|
||||
x = _Transpose(x, {0, 2, 3, 1});
|
||||
auto outputShape = _Concat({batch_n, kernel_h, kernel_w, _Multiply(input_c, multip)}, 0);
|
||||
// NACM => NHWC (NCMA => NCHW)
|
||||
std::unique_ptr<OpT> reshape(new OpT);
|
||||
reshape->type = OpType_Reshape;
|
||||
reshape->name = expr->name() + "_Reshape";
|
||||
reshape->main.type = OpParameter_Reshape;
|
||||
reshape->main.value = new ReshapeT;
|
||||
reshape->main.AsReshape()->dimType = MNN_DATA_FORMAT_NHWC;
|
||||
return (Expr::create(reshape.get(), {x, outputShape}));
|
||||
}
|
||||
};
|
||||
|
||||
class DeconvolutionTransform : public TFExtraManager::Transform {
|
||||
public:
|
||||
virtual EXPRP onExecute(EXPRP expr) const override {
|
||||
auto op = expr->get();
|
||||
bool depthwise = false;
|
||||
{
|
||||
std::unique_ptr<ExtraT> extraT(op->main_as_Extra()->UnPack());
|
||||
if (extraT->type == "DepthwiseConv2dNativeBackpropInput") {
|
||||
depthwise = true;
|
||||
}
|
||||
}
|
||||
auto inputs = expr->inputs();
|
||||
auto weight = inputs[1];
|
||||
auto weightInfo = weight->getInfo();
|
||||
auto weightTensorData = weight->readMap<float>();
|
||||
if (nullptr == weightInfo || nullptr == weightTensorData) {
|
||||
MNN_ERROR("For %s convolution weight is not const\n", expr->name().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<Convolution2DT> convolution2D(new MNN::Convolution2DT);
|
||||
int kh = weightInfo->dim[0];
|
||||
int kw = weightInfo->dim[1];
|
||||
int num_input = weightInfo->dim[2];
|
||||
int num_output = weightInfo->dim[3];
|
||||
weight = _Transpose(weight, {3, 2, 0, 1});
|
||||
weightInfo = weight->getInfo();
|
||||
weightTensorData = weight->readMap<float>();
|
||||
convolution2D->weight.resize(weightInfo->size);
|
||||
::memcpy(convolution2D->weight.data(), weightTensorData, weightInfo->size * sizeof(float));
|
||||
convolution2D->bias.resize(num_input);
|
||||
std::fill(convolution2D->bias.begin(), convolution2D->bias.end(), 0.0f);
|
||||
convolution2D->common.reset(new MNN::Convolution2DCommonT);
|
||||
auto common = convolution2D->common.get();
|
||||
|
||||
common->relu = false;
|
||||
common->group = 1;
|
||||
common->outputCount = num_input;
|
||||
common->inputCount = num_output;
|
||||
common->kernelX = kw;
|
||||
common->kernelY = kh;
|
||||
common->padX = 0;
|
||||
common->padY = 0;
|
||||
bool success = _writeCommonAttr(common, op->main_as_Extra(), op->name()->str());
|
||||
if (!success) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<OpT> newOp(new OpT);
|
||||
newOp->name = expr->name();
|
||||
newOp->type = OpType_Deconvolution;
|
||||
if (depthwise) {
|
||||
newOp->type = OpType_DeconvolutionDepthwise;
|
||||
}
|
||||
newOp->main.type = OpParameter_Convolution2D;
|
||||
newOp->main.value = convolution2D.release();
|
||||
if (inputs.size() == 2) {
|
||||
return Expr::create(newOp.get(), {inputs[0]}, 1);
|
||||
}
|
||||
MNN_ASSERT(inputs.size() == 3);
|
||||
auto newExpr = Expr::create(newOp.get(), {inputs[2]}, 1);
|
||||
/* check shape consistent between tf's output_shape attribute and MNN inferred output shape
|
||||
* When stride > 1, one output-shape can be reached from (stride - 1) input-shapes
|
||||
*/
|
||||
auto output = Variable::create(newExpr);
|
||||
auto outputInfo = output->getInfo();
|
||||
auto realOutputShape = inputs[0]->readMap<int>();
|
||||
if (nullptr != outputInfo && nullptr != realOutputShape) {
|
||||
int inferHeight = outputInfo->dim[2], inferWidth = outputInfo->dim[3]; // MNN format NCHW
|
||||
if (outputInfo->order == NHWC) {
|
||||
inferWidth = outputInfo->dim[2];
|
||||
inferHeight = outputInfo->dim[1];
|
||||
}
|
||||
int realHeight = realOutputShape[1], realWidth = realOutputShape[2]; // tf format NHWC
|
||||
if (realHeight != inferHeight || realWidth != inferWidth) {
|
||||
MNN_ERROR("==== output_shape is not consistent with inferred output shape in MNN. ====\n");
|
||||
MNN_ERROR("====(height,width): (%d,%d) vs (%d,%d)\n ====", realHeight, realWidth, inferHeight,
|
||||
inferWidth);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return newExpr;
|
||||
}
|
||||
};
|
||||
|
||||
class Dilation2DTransform : public TFExtraManager::Transform {
|
||||
public:
|
||||
virtual EXPRP onExecute(EXPRP expr) const override {
|
||||
auto op = expr->get();
|
||||
auto inputs = expr->inputs();
|
||||
auto weight = inputs[1];
|
||||
auto weightInfo = weight->getInfo();
|
||||
auto weightTensorData = weight->readMap<float>();
|
||||
if (nullptr == weightInfo || nullptr == weightTensorData) {
|
||||
MNN_ERROR("For %s convolution weight is not const\n", expr->name().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<Convolution2DT> convolution2D(new MNN::Convolution2DT);
|
||||
int kh = weightInfo->dim[0];
|
||||
int kw = weightInfo->dim[1];
|
||||
int depth = weightInfo->dim[2];
|
||||
weight = _Transpose(weight, {2, 0, 1});
|
||||
weightInfo = weight->getInfo();
|
||||
weightTensorData = weight->readMap<float>();
|
||||
convolution2D->weight.resize(weightInfo->size);
|
||||
::memcpy(convolution2D->weight.data(), weightTensorData, weightInfo->size * sizeof(float));
|
||||
convolution2D->common.reset(new MNN::Convolution2DCommonT);
|
||||
auto common = convolution2D->common.get();
|
||||
common->outputCount = depth;
|
||||
common->kernelX = kw;
|
||||
common->kernelY = kh;
|
||||
|
||||
bool success = _writeCommonAttr(common, op->main_as_Extra(), op->name()->str());
|
||||
if (!success) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<OpT> newOp(new OpT);
|
||||
newOp->name = expr->name();
|
||||
newOp->type = OpType_Dilation2D;
|
||||
newOp->main.type = OpParameter_Convolution2D;
|
||||
newOp->main.value = convolution2D.release();
|
||||
|
||||
return Expr::create(newOp.get(), {inputs[0]}, 1);
|
||||
}
|
||||
};
|
||||
|
||||
static auto gRegister = []() {
|
||||
TFExtraManager::get()->insert("Conv2D", std::shared_ptr<TFExtraManager::Transform>(new ConvolutionTransform));
|
||||
TFExtraManager::get()->insert("Conv2DBackpropInput",
|
||||
std::shared_ptr<TFExtraManager::Transform>(new DeconvolutionTransform));
|
||||
TFExtraManager::get()->insert("DepthwiseConv2dNative",
|
||||
std::shared_ptr<TFExtraManager::Transform>(new ConvolutionDepthwiseTransform));
|
||||
TFExtraManager::get()->insert("DepthwiseConv2dNativeBackpropInput",
|
||||
std::shared_ptr<TFExtraManager::Transform>(new DeconvolutionTransform));
|
||||
TFExtraManager::get()->insert("Dilation2D", std::shared_ptr<TFExtraManager::Transform>(new Dilation2DTransform));
|
||||
return true;
|
||||
}();
|
||||
} // namespace Express
|
||||
} // namespace MNN
|
||||
Reference in New Issue
Block a user