Broadcasting from ONNX

This commit is contained in:
Dmitry Kurtaev
2020-03-05 23:53:50 +03:00
parent a694e5074f
commit 9e332dc5fb
3 changed files with 111 additions and 62 deletions
+31 -20
View File
@@ -46,14 +46,14 @@ public:
{
std::vector<Mat> inputs;
inputs_arr.getMatVector(inputs);
hasWeights = blobs.size() == 2 || (blobs.size() == 1 && !hasBias);
hasWeights = blobs.size() == 2 || (blobs.size() <= 1 && !hasBias);
CV_Assert((inputs.size() == 2 && blobs.empty()) || blobs.size() == (int)hasWeights + (int)hasBias);
}
virtual bool supportBackend(int backendId) CV_OVERRIDE
{
return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_HALIDE ||
(backendId == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 && axis == 1) ||
(backendId == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 && axis == 1 && !blobs.empty()) ||
(backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && axis > 0);
}
@@ -78,10 +78,9 @@ public:
Mat &outBlob = outputs[0];
// There is a mode when we multiply a first blob by a second one
// instead of trainable weights.
Mat weights = blobs.empty() ? inputs[1] : (hasWeights ? blobs[0] : Mat());
Mat bias = hasBias ? blobs.back().reshape(1, 1) : Mat();
if (!weights.empty())
weights = weights.reshape(1, 1);
Mat weights = hasWeights ? (blobs.empty() ? inputs[1] : blobs[0]).reshape(1, 1) : Mat();;
Mat bias = hasBias ? (blobs.empty() ? inputs[1] : blobs.back()).reshape(1, 1) : Mat();
MatShape inpShape = shape(inpBlob);
const int numWeights = !weights.empty() ? weights.total() : bias.total();
CV_Assert(numWeights != 0);
@@ -229,28 +228,40 @@ public:
#ifdef HAVE_DNN_NGRAPH
virtual Ptr<BackendNode> initNgraph(const std::vector<Ptr<BackendWrapper> >& inputs, const std::vector<Ptr<BackendNode> >& nodes) CV_OVERRIDE
{
CV_Assert(!blobs.empty());
const size_t numChannels = blobs[0].total();
auto ieInpNode = nodes[0].dynamicCast<InfEngineNgraphNode>()->node;
auto ieInpNode0 = nodes[0].dynamicCast<InfEngineNgraphNode>()->node;
auto ieInpNode1 = nodes.size() > 1 ? nodes[1].dynamicCast<InfEngineNgraphNode>()->node : nullptr;
std::vector<size_t> shape(ieInpNode->get_shape().size(), 1);
size_t numChannels = 1;
if (blobs.empty())
for (const size_t& dim : ieInpNode1->get_shape())
numChannels *= dim;
else
numChannels = blobs[0].total();
std::vector<size_t> shape(ieInpNode0->get_shape().size(), 1);
int cAxis = clamp(axis, shape.size());
shape[cAxis] = numChannels;
auto node = ieInpNode;
auto node = ieInpNode0;
if (hasWeights)
{
auto weight = std::make_shared<ngraph::op::Constant>(ngraph::element::f32,
ngraph::Shape(shape), blobs[0].data);
auto weight = blobs.empty() ? ieInpNode1 :
std::make_shared<ngraph::op::Constant>(ngraph::element::f32, ngraph::Shape(shape), blobs[0].data);
node = std::make_shared<ngraph::op::v1::Multiply>(node, weight, ngraph::op::AutoBroadcastType::NUMPY);
}
if (hasBias || !hasWeights)
{
auto bias = hasBias ?
std::make_shared<ngraph::op::Constant>(ngraph::element::f32,
ngraph::Shape(shape), blobs.back().data) :
std::make_shared<ngraph::op::Constant>(ngraph::element::f32,
ngraph::Shape(shape), std::vector<float>(numChannels, 0).data());
std::shared_ptr<ngraph::Node> bias;
if (hasBias)
{
bias = blobs.empty() ? ieInpNode1 :
std::make_shared<ngraph::op::Constant>(ngraph::element::f32,
ngraph::Shape(shape), blobs.back().data);
}
else
bias = std::make_shared<ngraph::op::Constant>(ngraph::element::f32,
ngraph::Shape(shape), std::vector<float>(numChannels, 0).data());
node = std::make_shared<ngraph::op::v1::Add>(node, bias, ngraph::op::AutoBroadcastType::NUMPY);
}
return Ptr<BackendNode>(new InfEngineNgraphNode(node));
@@ -259,8 +270,8 @@ public:
void getScaleShift(Mat& scale, Mat& shift) const CV_OVERRIDE
{
scale = hasWeights ? blobs[0] : Mat();
shift = hasBias ? blobs.back() : Mat();
scale = (hasWeights && !blobs.empty()) ? blobs[0] : Mat();
shift = (hasBias && !blobs.empty()) ? blobs.back() : Mat();
}
virtual int64 getFLOPS(const std::vector<MatShape> &inputs,
+63 -18
View File
@@ -427,24 +427,57 @@ void ONNXImporter::populateNet(Net dstNet)
}
layerParams.type = "Slice";
}
else if (layer_type == "Add" || layer_type == "Sum")
else if (layer_type == "Add" || layer_type == "Sum" || layer_type == "Sub")
{
bool isSub = layer_type == "Sub";
CV_CheckEQ(node_proto.input_size(), 2, "");
if (layer_id.find(node_proto.input(1)) == layer_id.end())
{
Mat blob = getBlob(node_proto, constBlobs, 1);
blob = blob.reshape(1, 1);
if (blob.total() == 1) {
layerParams.type = "Power";
layerParams.set("shift", blob.at<float>(0));
layerParams.set("shift", (isSub ? -1 : 1) * blob.at<float>(0));
}
else {
layerParams.type = "Scale";
layerParams.set("bias_term", true);
layerParams.blobs.push_back(blob);
layerParams.blobs.push_back((isSub ? -1 : 1) * blob);
}
}
else {
else if (outShapes[node_proto.input(0)] == outShapes[node_proto.input(1)])
{
layerParams.type = "Eltwise";
if (isSub)
{
static float subCoeffs[] = {1.f, -1.f};
layerParams.set("coeff", DictValue::arrayReal<float*>(subCoeffs, 2));
}
}
else
{
if (isSub)
{
LayerParams powerParams;
powerParams.name = layerParams.name + "/neg";
powerParams.type = "Power";
powerParams.set("scale", -1);
//Create Power layer
int id = dstNet.addLayer(powerParams.name, powerParams.type, powerParams);
//Connect to input
layerId = layer_id.find(node_proto.input(1));
CV_Assert(layerId != layer_id.end());
dstNet.connect(layerId->second.layerId, layerId->second.outputId, id, 0);
//Add shape
layer_id.insert(std::make_pair(powerParams.name, LayerInfo(id, 0)));
outShapes[powerParams.name] = outShapes[node_proto.input(1)];
//Replace input to Power
node_proto.set_input(1, powerParams.name);
}
layerParams.type = "Scale";
layerParams.set("bias_term", true);
}
}
else if (layer_type == "Max")
@@ -452,19 +485,6 @@ void ONNXImporter::populateNet(Net dstNet)
layerParams.type = "Eltwise";
layerParams.set("operation", "max");
}
else if (layer_type == "Sub")
{
Mat blob = getBlob(node_proto, constBlobs, 1);
if (blob.total() == 1) {
layerParams.type = "Power";
layerParams.set("shift", -blob.at<float>(0));
}
else {
layerParams.type = "Scale";
layerParams.set("has_bias", true);
layerParams.blobs.push_back(-1.0f * blob.reshape(1, 1));
}
}
else if (layer_type == "Neg")
{
layerParams.type = "Power";
@@ -643,10 +663,35 @@ void ONNXImporter::populateNet(Net dstNet)
layerParams.type = "Scale";
}
}
else {
else if (outShapes[node_proto.input(0)] == outShapes[node_proto.input(1)])
{
layerParams.type = "Eltwise";
layerParams.set("operation", isDiv ? "div" : "prod");
}
else
{
if (isDiv)
{
LayerParams powerParams;
powerParams.name = layerParams.name + "/inv";
powerParams.type = "Power";
powerParams.set("power", -1);
//Create Power layer
int id = dstNet.addLayer(powerParams.name, powerParams.type, powerParams);
//Connect to input
layerId = layer_id.find(node_proto.input(1));
CV_Assert(layerId != layer_id.end());
dstNet.connect(layerId->second.layerId, layerId->second.outputId, id, 0);
//Add shape
layer_id.insert(std::make_pair(powerParams.name, LayerInfo(id, 0)));
outShapes[powerParams.name] = outShapes[node_proto.input(1)];
//Replace input to Power
node_proto.set_input(1, powerParams.name);
}
layerParams.type = "Scale";
}
if (!haveVariables)
{