[CPU] Interpolate operation implementation (#1465)

This commit is contained in:
Chenhu Wang authored and GitHub committed 2020-09-04 09:54:05 +03:00
1 parent 021941bf03
commit 479e13e713
11 files changed
+2693 -3

No files matched your search

@@ -45,6 +45,7 @@ set(LAYERS
${CMAKE_CURRENT_SOURCE_DIR}/nodes/mkldnn_resample_node.cpp
${CMAKE_CURRENT_SOURCE_DIR}/nodes/mkldnn_normalize_node.cpp
${CMAKE_CURRENT_SOURCE_DIR}/nodes/mkldnn_scatter_update_node.cpp
${CMAKE_CURRENT_SOURCE_DIR}/nodes/mkldnn_interpolate_node.cpp
${CMAKE_CURRENT_SOURCE_DIR}/nodes/list.cpp
${CMAKE_CURRENT_SOURCE_DIR}/nodes/batch_to_space.cpp
@@ -17,6 +17,7 @@
#include "nodes/mkldnn_quantize_node.h"
#include "nodes/mkldnn_mvn_node.h"
#include "nodes/mkldnn_resample_node.h"
#include "nodes/mkldnn_interpolate_node.h"
#include <blob_factory.hpp>
#include <legacy/ie_layers_internal.hpp>
@@ -132,6 +133,9 @@ void MKLDNNGraphOptimizer::ApplyCommonGraphOptimizations(MKLDNNGraph &graph) {
FuseResampleAndSimpleOperation(graph);
graph.RemoveDroppedNodes();
FuseInterpolateAndSimpleOperation(graph);
graph.RemoveDroppedNodes();
FuseNormalizeAndSimpleOperation(graph);
graph.RemoveDroppedNodes();
@@ -1731,6 +1735,74 @@ void MKLDNNGraphOptimizer::FuseResampleAndSimpleOperation(MKLDNNGraph &graph) {
}
}
void MKLDNNGraphOptimizer::FuseInterpolateAndSimpleOperation(MKLDNNGraph &graph) {
auto removeEdge = [](MKLDNNGraph &graph, MKLDNNEdgePtr& edge) {
auto& edges = graph.GetEdges();
for (auto it = edges.begin(); it != edges.end(); it++) {
if ((*it) == edge) {
edges.erase(it);
return;
}
}
};
auto& graphNodes = graph.GetNodes();
auto isSuitableParentNode = [](MKLDNNNodePtr node) {
bool isSuitable = (node->getType() == Interpolate);
if (isSuitable) {
return node->getChildEdges().size() == 1;
} else {
return false;
}
};
auto isSutableChildNode = [&](MKLDNNNodePtr parentNode, MKLDNNNodePtr childNode) {
// Avoid cycle dependencies
for (auto &childParentEdge : childNode->getParentEdges()) {
for (auto &parentParentEdge : parentNode->getParentEdges()) {
if (childParentEdge.lock()->getParent() == parentParentEdge.lock()->getParent())
return false;
}
}
if (!childNode->getFusedWith().empty())
return false;
auto interpolateNode = dynamic_cast<MKLDNNInterpolateNode*>(parentNode.get());
return interpolateNode->canFuse(childNode);
};
auto parent = graphNodes.begin();
while (parent != graphNodes.end()) {
auto parentNode = *parent;
if (!isSuitableParentNode(parentNode)) {
parent++;
continue;
}
auto childNode = parentNode->getChildEdgeAt(0)->getChild();
auto interpolateNode = dynamic_cast<MKLDNNInterpolateNode*>(parentNode.get());
if (!isSutableChildNode(parentNode, childNode)) {
parent++;
continue;
}
parentNode->fuseWith(childNode);
if (childNode->getType() == Quantize || childNode->getType() == Depthwise || childNode->getType() == Activation) {
auto parentEdges = childNode->parentEdges;
for (auto &parentEdge : parentEdges) {
auto p_edge = parentEdge.lock();
if (p_edge->getParent()->getType() == Interpolate)
continue;
removeEdge(graph, p_edge);
}
}
graph.DropNode(childNode);
}
}
void MKLDNNGraphOptimizer::FuseNormalizeAndSimpleOperation(MKLDNNGraph &graph) {
auto isOneOf = [&](mkldnn::algorithm alg, std::vector<mkldnn::algorithm> algs) {
for (auto a : algs) {
@@ -43,6 +43,7 @@ private:
#endif
void FuseMVNAndSimpleOperation(MKLDNNGraph &graph);
void FuseResampleAndSimpleOperation(MKLDNNGraph &graph);
void FuseInterpolateAndSimpleOperation(MKLDNNGraph &graph);
void FuseNormalizeAndSimpleOperation(MKLDNNGraph &graph);
void RemoveIdentityOperator(MKLDNNGraph& graph);
@@ -44,6 +44,7 @@
#include <nodes/mkldnn_normalize_node.h>
#include <nodes/mkldnn_tensoriterator_node.h>
#include <nodes/mkldnn_scatter_update_node.h>
#include <nodes/mkldnn_interpolate_node.h>
#include <mkldnn_types.h>
#include "mkldnn_extension_utils.h"
@@ -122,6 +123,7 @@ static const InferenceEngine::details::caseless_unordered_map<std::string, Type>
{ "ScatterUpdate", ScatterUpdate},
{ "ScatterElementsUpdate", ScatterElementsUpdate},
{ "ScatterNDUpdate", ScatterNDUpdate},
{ "Interpolate", Interpolate},
};
Type TypeFromName(const std::string type) {
@@ -76,7 +76,8 @@ enum Type {
Normalize,
ScatterUpdate,
ScatterElementsUpdate,
ScatterNDUpdate
ScatterNDUpdate,
Interpolate
};
Type TypeFromName(const std::string type);
@@ -165,6 +166,8 @@ static std::string NameFromType(Type type) {
return "ScatterElementsUpdate";
case ScatterNDUpdate:
return "ScatterNDUpdate";
case Interpolate:
return "Interpolate";
default:
return "Unknown";
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,180 @@
// Copyright (C) 2020 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#pragma once
#include <ie_common.h>
#include <mkldnn_node.h>
#include <string>
#include <memory>
#include <vector>
using namespace InferenceEngine;
namespace MKLDNNPlugin {
enum InterpolateLayoutType {
planar,
block,
by_channel
};
enum InterpolateMode {
nearest,
linear,
linear_onnx,
cubic
};
enum InterpolateCoordTransMode {
half_pixel,
pytorch_half_pixel,
asymmetric,
tf_half_pixel_for_nn,
align_corners
};
enum class InterpolateNearestMode {
round_prefer_floor,
round_prefer_ceil,
floor,
ceil,
simple
};
struct jit_interpolate_config_params {
InterpolateLayoutType layout;
InterpolateMode mode;
mkldnn::memory::data_type src_dt;
mkldnn::memory::data_type dst_dt;
int src_data_size;
int dst_data_size;
int indices_size;
int IH, IW, OH, OW;
};
struct jit_interpolate_call_args {
const void *src;
const void *srcTR;
const void *srcBL;
const void *srcBR;
const float *weight;
const float *weightR;
const float *weightT;
const float *weightB;
const int *index;
void *dst;
size_t work_amount;
size_t oc_off;
};
struct jit_uni_interpolate_kernel {
void (*ker_)(const jit_interpolate_call_args *);
void operator()(const jit_interpolate_call_args *args) {
assert(ker_);
ker_(args);
}
explicit jit_uni_interpolate_kernel(jit_interpolate_config_params jcp, const mkldnn_primitive_attr &attr) : ker_(nullptr), jcp_(jcp), attr_(attr) {}
virtual ~jit_uni_interpolate_kernel() {}
jit_interpolate_config_params jcp_;
const mkldnn_primitive_attr &attr_;
};
class MKLDNNInterpolateNode : public MKLDNNNode {
public:
MKLDNNInterpolateNode(const InferenceEngine::CNNLayerPtr& layer, const mkldnn::engine& eng, MKLDNNWeightsSharing::Ptr &cache);
~MKLDNNInterpolateNode() override = default;
void getSupportedDescriptors() override;
void initSupportedPrimitiveDescriptors() override;
void createPrimitive() override;
bool created() const override;
void execute(mkldnn::stream strm) override;
bool canBeInPlace() const override {
return false;
}
bool canFuse(const MKLDNNNodePtr& node) const;
private:
// nearest neighbor
void NNPlanar(const uint8_t *in_ptr_, uint8_t *out_ptr_, int B, int C, int ID, int IH, int IW,
float fx, float fy, float fz, int OD, int OH, int OW);
void NNCGathered(const uint8_t *in_ptr_, uint8_t *out_ptr_, int B, int C, int ID, int IH, int IW,
float fx, float fy, float fz, int OD, int OH, int OW);
void NNRef(const uint8_t *in_ptr_, uint8_t *out_ptr_, int B, int C, int ID, int IH, int IW,
float fx, float fy, float fz, int OD, int OH, int OW);
// onnx linear
void linearOnnxPlanar(const uint8_t *in_ptr_, uint8_t *out_ptr_, int B, int C, int IH, int IW,
float fx, float fy, int OH, int OW);
void linearOnnxCGathered(const uint8_t *in_ptr_, uint8_t *out_ptr_, int B, int C, int IH, int IW,
float fx, float fy, int OH, int OW);
void linearOnnxRef(const uint8_t *in_ptr_, uint8_t *out_ptr_, int B, int C, int IH, int IW,
float fx, float fy, int OH, int OW);
// linear
void linearInterpolation(const uint8_t *in_ptr_, uint8_t *out_ptr_, int B, int C, int ID, int IH, int IW,
float fx, float fy, float fz, int OD, int OH, int OW, int kernel_width, bool antialias);
// cubic
std::vector<float> getCubicCoeffs(float mantissa, float a);
void cubic(const uint8_t *in_ptr_, uint8_t *out_ptr_, int B, int C, int IH, int IW,
float fx, float fy, int OH, int OW, float a);
void buildTblNN(SizeVector& srcDimPad5d, SizeVector& dstDim5d, std::vector<float>& dataScales, InterpolateLayoutType layout);
void buildTblLinearOnnx(SizeVector& srcDimPad5d, SizeVector& dstDim5d, std::vector<float>& dataScales, InterpolateLayoutType layout);
void buidTblLinear(SizeVector& srcDimPad5d, SizeVector& dstDim5d, std::vector<float>& dataScales, int kernel_width, bool antialias);
void buidTblCubic(SizeVector& srcDimPad5d, SizeVector& dstDim5d, std::vector<float>& dataScales, float cubicCoeff);
void setPostOps(mkldnn::primitive_attr &attr, bool initWeights = false);
inline float coordTransToInput(int outCoord, float scale, int inShape, int outShape);
inline int nearestRound(float origin, bool isDownsample);
float getValue(const uint8_t *base, size_t offset, InferenceEngine::Precision prec);
void setValue(uint8_t *base, size_t offset, float value, InferenceEngine::Precision prec);
SizeVector getPaddedInputShape();
std::vector<float> getScales();
const size_t DATA_ID = 0;
const size_t TARGET_SHAPE_ID = 1;
const size_t SCALES_ID = 2;
const size_t AXES_ID = 3;
const int LINEAR_KERNEL = 2;
const int CUBIC_GRID_LEN = 4;
InterpolateMode mode;
InterpolateCoordTransMode coordTransMode = InterpolateCoordTransMode::half_pixel;
bool antialias = false;
std::vector<int> padBegin;
std::vector<int> padEnd;
bool hasPad = false;
InterpolateNearestMode nearestMode = InterpolateNearestMode::round_prefer_floor;
float cubeCoeff = -0.75;
bool isAxesSpecified = false;
// axes and scales from buffer, partical size.
std::vector<int> axes;
std::vector<float> scales;
// target shape is dst dim, full size.
SizeVector dstDim;
std::string shapeInferMode;
SizeVector srcDim;
SizeVector srcDimPad;
mkldnn::primitive_attr attr;
std::vector<MKLDNNMemoryPtr> PostOpsIntBlobMemory;
InferenceEngine::Precision inputPrec, outputPrec;
size_t srcDataSize, dstDataSize;
std::vector<int> indexTable;
std::shared_ptr<jit_uni_interpolate_kernel> interpolateKernel;
};
} // namespace MKLDNNPlugin
@@ -49,8 +49,6 @@ std::vector<std::string> disabledTestPatterns() {
R"(.*ActivationLayerTest.*Ceiling.*)",
// TODO: Issue: 32032
R"(.*ActivationParamLayerTest.*)",
// TODO: Issue: 30999 (Implement Interpolate reference in NGraph)
R"(.*InterpolateLayerTest.*)",
// TODO: Issue: 37862
R"(.*ReverseSequenceLayerTest.*netPRC=(I8|U8).*)"
};
@@ -0,0 +1,190 @@
// Copyright (C) 2020 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include <single_layer_tests/interpolate.hpp>
#include "test_utils/cpu_test_utils.hpp"
using namespace InferenceEngine;
using namespace CPUTestUtils;
namespace CPULayerTestsDefinitions {
typedef std::tuple<
LayerTestsDefinitions::InterpolateLayerTestParams,
CPUSpecificParams> InterpolateLayerCPUTestParamsSet;
class InterpolateLayerCPUTest : public testing::WithParamInterface<InterpolateLayerCPUTestParamsSet>,
virtual public LayerTestsUtils::LayerTestsCommon, public CPUTestsBase {
public:
static std::string getTestCaseName(testing::TestParamInfo<InterpolateLayerCPUTestParamsSet> obj) {
LayerTestsDefinitions::InterpolateLayerTestParams basicParamsSet;
CPUSpecificParams cpuParams;
std::tie(basicParamsSet, cpuParams) = obj.param;
std::ostringstream result;
result << LayerTestsDefinitions::InterpolateLayerTest::getTestCaseName(testing::TestParamInfo<LayerTestsDefinitions::InterpolateLayerTestParams>(
basicParamsSet, 0));
result << CPUTestsBase::getTestCaseName(cpuParams);
return result.str();
}
protected:
void SetUp() {
LayerTestsDefinitions::InterpolateLayerTestParams basicParamsSet;
CPUSpecificParams cpuParams;
std::tie(basicParamsSet, cpuParams) = this->GetParam();
std::tie(inFmts, outFmts, priority, selectedType) = cpuParams;
LayerTestsDefinitions::InterpolateSpecificParams interpolateParams;
std::vector<size_t> inputShape;
std::vector<size_t> targetShape;
auto netPrecision = InferenceEngine::Precision::UNSPECIFIED;
std::tie(interpolateParams, netPrecision, inputShape, targetShape, targetDevice) = basicParamsSet;
ngraph::op::v4::Interpolate::InterpolateMode mode;
ngraph::op::v4::Interpolate::CoordinateTransformMode coordinateTransformMode;
ngraph::op::v4::Interpolate::NearestMode nearestMode;
bool antialias;
std::vector<size_t> padBegin, padEnd;
double cubeCoef;
std:tie(mode, coordinateTransformMode, nearestMode, antialias, padBegin, padEnd, cubeCoef) = interpolateParams;
using ShapeCalcMode = ngraph::op::v4::Interpolate::ShapeCalcMode;
ShapeCalcMode shape_calc_mode = ShapeCalcMode::sizes;
auto ngPrc = FuncTestUtils::PrecisionUtils::convertIE2nGraphPrc(netPrecision);
auto params = ngraph::builder::makeParams(ngPrc, {inputShape});
auto constant = ngraph::opset3::Constant(ngraph::element::Type_t::i64, {targetShape.size()}, targetShape);
std::vector<float> scales(targetShape.size(), 1.0f);
auto scales_const = ngraph::opset3::Constant(ngraph::element::Type_t::f32, {scales.size()}, scales);
auto scalesInput = std::make_shared<ngraph::opset3::Constant>(scales_const);
auto secondaryInput = std::make_shared<ngraph::opset3::Constant>(constant);
ngraph::op::v4::Interpolate::InterpolateAttrs interpolateAttributes{mode, shape_calc_mode, padBegin,
padEnd, coordinateTransformMode, nearestMode, antialias, cubeCoef};
auto interpolate = std::make_shared<ngraph::op::v4::Interpolate>(params[0],
secondaryInput,
scalesInput,
interpolateAttributes);
interpolate->get_rt_info() = CPUTestsBase::setCPUInfo(inFmts, outFmts, priority);
const ngraph::ResultVector results{std::make_shared<ngraph::opset3::Result>(interpolate)};
function = std::make_shared<ngraph::Function>(results, params, "interpolate");
}
std::vector<cpu_memory_format_t> inFmts, outFmts;
std::vector<std::string> priority;
std::string selectedType;
};
TEST_P(InterpolateLayerCPUTest, CompareWithRefs) {
SKIP_IF_CURRENT_TEST_IS_DISABLED()
Run();
CheckCPUImpl(executableNetwork, "interpolate", inFmts, outFmts, selectedType);
}
namespace {
/* CPU PARAMS */
std::vector<CPUSpecificParams> filterCPUInfoForDevice() {
std::vector<CPUSpecificParams> resCPUParams;
if (with_cpu_x86_avx512f()) {
resCPUParams.push_back(CPUSpecificParams{{nChw16c}, {nChw16c}, {}, "unknown"});
} else if (with_cpu_x86_sse42()) {
resCPUParams.push_back(CPUSpecificParams{{nChw8c}, {nChw8c}, {}, "unknown"});
}
if (with_cpu_x86_sse42()) {
resCPUParams.push_back(CPUSpecificParams{{nhwc}, {nhwc}, {}, "unknown"});
}
resCPUParams.push_back(CPUSpecificParams{{nchw}, {nchw}, {}, "unknown"});
return resCPUParams;
}
/* ========== */
const std::vector<InferenceEngine::Precision> netPrecisions = {
InferenceEngine::Precision::FP32
};
const std::vector<ngraph::op::v4::Interpolate::CoordinateTransformMode> coordinateTransformModes = {
ngraph::op::v4::Interpolate::CoordinateTransformMode::tf_half_pixel_for_nn,
ngraph::op::v4::Interpolate::CoordinateTransformMode::pytorch_half_pixel,
ngraph::op::v4::Interpolate::CoordinateTransformMode::half_pixel,
ngraph::op::v4::Interpolate::CoordinateTransformMode::asymmetric,
ngraph::op::v4::Interpolate::CoordinateTransformMode::align_corners,
};
const std::vector<ngraph::op::v4::Interpolate::NearestMode> nearestModes = {
ngraph::op::v4::Interpolate::NearestMode::simple,
ngraph::op::v4::Interpolate::NearestMode::round_prefer_floor,
ngraph::op::v4::Interpolate::NearestMode::floor,
ngraph::op::v4::Interpolate::NearestMode::ceil,
ngraph::op::v4::Interpolate::NearestMode::round_prefer_ceil,
};
const std::vector<ngraph::op::v4::Interpolate::NearestMode> defNearestModes = {
ngraph::op::v4::Interpolate::NearestMode::round_prefer_floor,
};
const std::vector<std::vector<size_t>> pads = {
{0, 0, 0, 0},
};
const std::vector<bool> antialias = {
false,
};
const std::vector<double> cubeCoefs = {
-0.75f,
};
const auto interpolateCasesNN = ::testing::Combine(
::testing::Values(ngraph::op::v4::Interpolate::InterpolateMode::nearest),
::testing::ValuesIn(coordinateTransformModes),
::testing::ValuesIn(nearestModes),
::testing::ValuesIn(antialias),
::testing::ValuesIn(pads),
::testing::ValuesIn(pads),
::testing::ValuesIn(cubeCoefs));
const auto interpolateCasesLinearOnnx = ::testing::Combine(
::testing::Values(ngraph::op::v4::Interpolate::InterpolateMode::linear_onnx),
::testing::ValuesIn(coordinateTransformModes),
::testing::ValuesIn(defNearestModes),
::testing::ValuesIn(antialias),
::testing::ValuesIn(pads),
::testing::ValuesIn(pads),
::testing::ValuesIn(cubeCoefs));
INSTANTIATE_TEST_CASE_P(InterpolateNN_Layout_Test, InterpolateLayerCPUTest,
::testing::Combine(
::testing::Combine(
interpolateCasesNN,
::testing::ValuesIn(netPrecisions),
::testing::Values(std::vector<size_t>({1, 20, 40, 40})),
::testing::Values(std::vector<size_t>({1, 20, 50, 60})),
::testing::Values(CommonTestUtils::DEVICE_CPU)),
::testing::ValuesIn(filterCPUInfoForDevice())),
InterpolateLayerCPUTest::getTestCaseName);
INSTANTIATE_TEST_CASE_P(InterpolateLinearOnnx_Layout_Test, InterpolateLayerCPUTest,
::testing::Combine(
::testing::Combine(
interpolateCasesLinearOnnx,
::testing::ValuesIn(netPrecisions),
::testing::Values(std::vector<size_t>({1, 20, 40, 40})),
::testing::Values(std::vector<size_t>({1, 20, 50, 60})),
::testing::Values(CommonTestUtils::DEVICE_CPU)),
::testing::ValuesIn(filterCPUInfoForDevice())),
InterpolateLayerCPUTest::getTestCaseName);
} // namespace
} // namespace CPULayerTestsDefinitions
@@ -10,9 +10,11 @@ const char *CPUTestsBase::cpu_fmt2str(cpu_memory_format_t v) {
if (v == nchw) return "nchw";
if (v == nChw8c) return "nChw8c";
if (v == nChw16c) return "nChw16c";
if (v == nhwc) return "nhwc";
if (v == ncdhw) return "ncdhw";
if (v == nCdhw8c) return "nCdhw8c";
if (v == nCdhw16c) return "nCdhw16c";
if (v == ndhwc) return "ndhwc";
assert(!"unknown fmt");
return "undef";
}
@@ -26,9 +28,11 @@ cpu_memory_format_t CPUTestsBase::cpu_str2fmt(const char *str) {
CASE(nchw);
CASE(nChw8c);
CASE(nChw16c);
CASE(nhwc);
CASE(ncdhw);
CASE(nCdhw8c);
CASE(nCdhw16c);
CASE(ndhwc);
#undef CASE
assert(!"unknown memory format");
return undef;
@@ -18,9 +18,11 @@ namespace CPUTestUtils {
nchw,
nChw8c,
nChw16c,
nhwc,
ncdhw,
nCdhw8c,
nCdhw16c,
ndhwc,
undef
} cpu_memory_format_t;