[GNA] Fix for concat layer with >2 inputs (#1475)
* Fix for concat layer with more than 2 inputs Signed-off-by: Bartosz Sochacki <bartosz.sochacki@intel.com> * Fixed check if affine is used for crop layer Signed-off-by: Bartosz Sochacki <bartosz.sochacki@intel.com> * code cleanup for fix affine layer check Signed-off-by: Bartosz Sochacki <bartosz.sochacki@intel.com> * added test for concat layer with multiple inputs * simplified test to use less number of layers * fixed code style * fixed coding style * addressed review comments and one more issue that appeared during testing * fixed code style errors * scale factor propagation for concat layer with multiple inputs * fix for a case when all inputs to concat are activation layers * fix for linux compilation - C++14 is not enabled and fails on lambda with auto parameters * corrected current year in headers in concat multi input tests * fixes for code review issues raised by Denis Orlov * enabled integer mode computation in GNA concat multi input test * removed 1 space per review comment * a fix to fail when not all scale factors are equal * added GNA_DEVICE_MODE config to concat multi input test * corrected searching for a next input to concat layer * changed selection of 2nd candidate for source quant value * code style fix - else and brackets should be in the same line * small code improvement * fix for mixing line endings * addressed with endless requantization loop and fixed failing tests
This commit is contained in:
parent
135ae12b0d
commit
8b87e1a477
@ -382,91 +382,137 @@ class ScaleFactorPerLayer<InferenceEngine::ConcatLayer*> {
|
|||||||
if ( !concatLayer ) {
|
if ( !concatLayer ) {
|
||||||
THROW_GNA_EXCEPTION << "Incorrect Concat Layer pointer \n";
|
THROW_GNA_EXCEPTION << "Incorrect Concat Layer pointer \n";
|
||||||
}
|
}
|
||||||
auto in0 = InferenceEngine::CNNNetPrevLayer(concatLayer, 0);
|
|
||||||
auto in1 = InferenceEngine::CNNNetPrevLayer(concatLayer, 1);
|
if (concatLayer->insData.size() < 2) {
|
||||||
auto infoIn0 = LayerInfo(in0);
|
THROW_GNA_EXCEPTION << "Concat layer has unsupported number of incoming layers.";
|
||||||
auto infoIn1 = LayerInfo(in1);
|
}
|
||||||
auto quantParams0 = InferenceEngine::getInjectedData<QuantizedLayerParams>(in0);
|
|
||||||
auto quantParams1 = InferenceEngine::getInjectedData<QuantizedLayerParams>(in1);
|
|
||||||
GNAPluginNS::QuantizedLayerParams* sourceQuantParams = NULL;
|
|
||||||
auto quantData = InferenceEngine::getInjectedData<QuantizedLayerParams>(*concatLayer);
|
|
||||||
|
|
||||||
auto fp32eq = [](float p1, float p2) -> bool {
|
auto fp32eq = [](float p1, float p2) -> bool {
|
||||||
return (std::abs(p1 - p2) <= 0.00001f * std::min(std::abs(p1), std::abs(p2)));
|
return (std::abs(p1 - p2) <= 0.00001f * std::min(std::abs(p1), std::abs(p2)));
|
||||||
};
|
};
|
||||||
|
|
||||||
// if both inputs have same quant value - trivial propagation
|
auto quantData = InferenceEngine::getInjectedData<QuantizedLayerParams>(*concatLayer);
|
||||||
if (fp32eq(quantParams0->_dst_quant.scale, quantParams1->_dst_quant.scale)) {
|
std::vector<InferenceEngine::CNNLayerPtr> inputLayers;
|
||||||
|
for (auto input_idx = 0; input_idx != concatLayer->insData.size(); input_idx++) {
|
||||||
|
inputLayers.push_back(InferenceEngine::CNNNetPrevLayer(concatLayer, input_idx));
|
||||||
|
}
|
||||||
|
|
||||||
|
// if all inputs have same quant value - trivial propagation
|
||||||
|
auto in0 = inputLayers.front();
|
||||||
|
auto quantParams0 = InferenceEngine::getInjectedData<QuantizedLayerParams>(in0);
|
||||||
|
auto scaleFactor = quantParams0->_dst_quant.scale;
|
||||||
|
auto scaleFactorCheck = [scaleFactor, &fp32eq](InferenceEngine::CNNLayerPtr& inputLayer) {
|
||||||
|
auto quantParams = InferenceEngine::getInjectedData<QuantizedLayerParams>(inputLayer);
|
||||||
|
return fp32eq(quantParams->_dst_quant.scale, scaleFactor);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (std::find_if_not(inputLayers.begin() + 1, inputLayers.end(), scaleFactorCheck) == inputLayers.end()) {
|
||||||
quantData->_dst_quant.scale = quantParams0->_dst_quant.scale;
|
quantData->_dst_quant.scale = quantParams0->_dst_quant.scale;
|
||||||
quantData->_src_quant.scale = quantParams0->_dst_quant.scale;
|
quantData->_src_quant.scale = quantParams0->_dst_quant.scale;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// support only cases when one of input is network input
|
|
||||||
if (infoIn0.isInput() && infoIn1.isInput()) {
|
// check if all inputs have the same quant value
|
||||||
THROW_GNA_EXCEPTION << "Two Input layers " << in0->name << "and" << in1->name << " has different scales in concat!!! \n";
|
auto inputLayerCheck = [](InferenceEngine::CNNLayerPtr& inputLayer) {
|
||||||
|
auto info = LayerInfo(inputLayer);
|
||||||
|
return info.isInput();
|
||||||
|
};
|
||||||
|
|
||||||
|
GNAPluginNS::QuantizedLayerParams* sourceQuantParams = nullptr;
|
||||||
|
auto firstInputIt = std::find_if(inputLayers.begin(), inputLayers.end(), inputLayerCheck);
|
||||||
|
if (firstInputIt != inputLayers.end()) {
|
||||||
|
auto quantParamsFirst = InferenceEngine::getInjectedData<QuantizedLayerParams>(*firstInputIt);
|
||||||
|
auto nextInputIt = firstInputIt + 1;
|
||||||
|
while ((nextInputIt = std::find_if(nextInputIt, inputLayers.end(), inputLayerCheck)) != inputLayers.end()) {
|
||||||
|
auto quantParamsSecond = InferenceEngine::getInjectedData<QuantizedLayerParams>(*nextInputIt);
|
||||||
|
if (!fp32eq(quantParamsSecond->_dst_quant.scale, quantParamsFirst->_dst_quant.scale)) {
|
||||||
|
THROW_GNA_EXCEPTION << "Two Input layers " << (*firstInputIt)->name
|
||||||
|
<< " and " << (*nextInputIt)->name << " have different scales in concat!!! \n";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int concatIdxToUpdate = -1;
|
// find a source quant value
|
||||||
|
// - 1st candidate - non-activation layer with non-1 scale factor
|
||||||
|
// - 2nd candidate - 1st layer with non-1 scale factor
|
||||||
|
auto sourceLayerCheck = [&fp32eq](InferenceEngine::CNNLayerPtr& inputLayer) {
|
||||||
|
auto quantParams = InferenceEngine::getInjectedData<QuantizedLayerParams>(inputLayer);
|
||||||
|
LayerInfo info(inputLayer);
|
||||||
|
return !info.isActivation() && !fp32eq(quantParams->_dst_quant.scale, 1.0f);
|
||||||
|
};
|
||||||
|
|
||||||
if (infoIn0.isInput()) {
|
static std::map<std::string, size_t> restarted_counter;
|
||||||
sourceQuantParams = quantParams0;
|
auto restartedCountIt = restarted_counter.find(concatLayer->name);
|
||||||
} else if (infoIn1.isInput()) {
|
if (restartedCountIt == restarted_counter.end()) {
|
||||||
concatIdxToUpdate = 0;
|
auto pos = restarted_counter.insert({ "concatLayer->name", 0 });
|
||||||
sourceQuantParams = quantParams1;
|
restartedCountIt = pos.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (restartedCountIt->second % 2 == 1) {
|
||||||
|
std::reverse(inputLayers.begin(), inputLayers.end());
|
||||||
|
}
|
||||||
|
++restartedCountIt->second;
|
||||||
|
|
||||||
|
auto sourceLayerIt = std::find_if(inputLayers.begin(), inputLayers.end(), sourceLayerCheck);
|
||||||
|
if (sourceLayerIt == inputLayers.end()) {
|
||||||
|
auto nonDefaultScaleFactor = [&fp32eq](InferenceEngine::CNNLayerPtr& inputLayer) {
|
||||||
|
auto quantParams = InferenceEngine::getInjectedData<QuantizedLayerParams>(inputLayer);
|
||||||
|
return !fp32eq(quantParams->_dst_quant.scale, 1.0f);
|
||||||
|
};
|
||||||
|
|
||||||
|
sourceLayerIt = std::find_if(inputLayers.begin(), inputLayers.end(), nonDefaultScaleFactor);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<size_t> concatIdxToUpdate;
|
||||||
|
if (sourceLayerIt != inputLayers.end()) {
|
||||||
|
auto quantParams = InferenceEngine::getInjectedData<QuantizedLayerParams>(*sourceLayerIt);
|
||||||
|
auto scaleFactor = quantParams->_dst_quant.scale;
|
||||||
|
sourceQuantParams = quantParams;
|
||||||
|
|
||||||
|
for (auto it = inputLayers.begin(); it != inputLayers.end(); ++it) {
|
||||||
|
auto quantParamsIn = InferenceEngine::getInjectedData<QuantizedLayerParams>(*it);
|
||||||
|
if (fp32eq(quantParamsIn->_dst_quant.scale, scaleFactor)) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// possible case when some of the concat inputs are free to select scale ex: const->concat<-affine
|
// possible case when some of the concat inputs are free to select scale ex: const->concat<-affine
|
||||||
if (quantParams1->_dst_quant.scale == 1.0) {
|
if (!fp32eq(quantParamsIn->_dst_quant.scale, 1.0f) && !LayerInfo(*it).isActivation()) {
|
||||||
quantParams1->_weights_quant = quantParams0->_dst_quant;
|
concatIdxToUpdate.insert(std::distance(inputLayers.begin(), it));
|
||||||
quantParams1->_dst_quant = quantParams0->_dst_quant;
|
|
||||||
|
|
||||||
sourceQuantParams = quantParams0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (quantParams0->_dst_quant.scale == 1.0) {
|
quantParamsIn->_weights_quant = quantParams->_dst_quant;
|
||||||
quantParams0->_weights_quant = quantParams1->_dst_quant;
|
quantParamsIn->_dst_quant = quantParams->_dst_quant;
|
||||||
quantParams0->_dst_quant = quantParams1->_dst_quant;
|
|
||||||
sourceQuantParams = quantParams1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!sourceQuantParams) {
|
|
||||||
auto in0LayerInfo = LayerInfo(in0);
|
|
||||||
auto in1LayerInfo = LayerInfo(in1);
|
|
||||||
if (in0LayerInfo.isActivation()) {
|
|
||||||
quantParams0->_weights_quant = quantParams1->_dst_quant;
|
|
||||||
quantParams0->_dst_quant = quantParams1->_dst_quant;
|
|
||||||
sourceQuantParams = quantParams1;
|
|
||||||
} else if (in1LayerInfo.isActivation()) {
|
|
||||||
quantParams1->_weights_quant = quantParams0->_dst_quant;
|
|
||||||
quantParams1->_dst_quant = quantParams0->_dst_quant;
|
|
||||||
sourceQuantParams = quantParams0;
|
|
||||||
} else {
|
|
||||||
THROW_GNA_LAYER_EXCEPTION(concatLayer) << "Concat quantization for " << in0->type << ": " << in0->name
|
|
||||||
<< " and " << in1->type << ": " << in1->name
|
|
||||||
<< " as inputs needs to be implemented! None of these inputs is an activation.\n";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fp32eq(quantParams0->_dst_quant.scale, quantParams1->_dst_quant.scale) && concatIdxToUpdate == -1) {
|
auto updatedScaleFactor = InferenceEngine::getInjectedData<QuantizedLayerParams>(in0)->_dst_quant.scale;
|
||||||
|
auto equalScaleFactor = [updatedScaleFactor, &fp32eq](InferenceEngine::CNNLayerPtr& inputLayer) {
|
||||||
|
auto quantParams = InferenceEngine::getInjectedData<QuantizedLayerParams>(inputLayer);
|
||||||
|
return fp32eq(quantParams->_dst_quant.scale, updatedScaleFactor);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto layerIt = std::find_if_not(inputLayers.begin() + 1, inputLayers.end(), equalScaleFactor);
|
||||||
|
if (layerIt != inputLayers.end()) {
|
||||||
THROW_GNA_EXCEPTION << "layers entered into concat have different scale factors" << concatLayer->name;
|
THROW_GNA_EXCEPTION << "layers entered into concat have different scale factors" << concatLayer->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
quantData->_dst_quant.scale = sourceQuantParams->_dst_quant.scale;
|
quantData->_dst_quant.scale = sourceQuantParams->_dst_quant.scale;
|
||||||
quantData->_src_quant.scale = sourceQuantParams->_dst_quant.scale;
|
quantData->_src_quant.scale = sourceQuantParams->_dst_quant.scale;
|
||||||
|
|
||||||
if (fp32eq(quantParams0->_dst_quant.scale, quantParams1->_dst_quant.scale) || concatIdxToUpdate == -1) {
|
if (layerIt == inputLayers.end() && concatIdxToUpdate.empty()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (auto& layerIdToUpdate : concatIdxToUpdate) {
|
||||||
auto destinationQuantParams = InferenceEngine::getInjectedData<QuantizedLayerParams>(*concatLayer);
|
auto destinationQuantParams = InferenceEngine::getInjectedData<QuantizedLayerParams>(*concatLayer);
|
||||||
destinationQuantParams->_dst_quant.scale = sourceQuantParams->_dst_quant.scale;
|
destinationQuantParams->_dst_quant.scale = sourceQuantParams->_dst_quant.scale;
|
||||||
|
|
||||||
InferenceEngine::CNNLayerPtr restartedLayer;
|
InferenceEngine::CNNLayerPtr restartedLayer;
|
||||||
// making a link activation possible without extra layer if first input to concat not a parent / indirect parent of second input
|
// making a link activation possible without extra layer if first input to concat not a parent / indirect parent of second input
|
||||||
// using ufs - upper first search
|
// using ufs - upper first search
|
||||||
gnalog() << "[UFS] searching for quantizeable layer prior: "<< concatLayer->name << ", via " << concatIdxToUpdate << "\n";
|
gnalog() << "[UFS] searching for quantizeable layer prior: " << concatLayer->name << ", via " << layerIdToUpdate << "\n";
|
||||||
|
|
||||||
CNNNetDFS(InferenceEngine::CNNLayerPtr(concatLayer, [](InferenceEngine::CNNLayer *) {}),
|
CNNNetDFS(InferenceEngine::CNNLayerPtr(concatLayer, [](InferenceEngine::CNNLayer*) {}),
|
||||||
[&restartedLayer, concatLayer](InferenceEngine::CNNLayerPtr layer) {
|
[&restartedLayer, concatLayer](InferenceEngine::CNNLayerPtr layer) {
|
||||||
gnalog() << "[UFS] from : " << concatLayer->name << " reached: " << layer->name;
|
gnalog() << "[UFS] from : " << concatLayer->name << " reached: " << layer->name;
|
||||||
// found that direct input to concat is a indirect parent of align filter - so no link required
|
// found that direct input to concat is a indirect parent of align filter - so no link required
|
||||||
@ -477,14 +523,14 @@ class ScaleFactorPerLayer<InferenceEngine::ConcatLayer*> {
|
|||||||
}
|
}
|
||||||
restartedLayer = layer;
|
restartedLayer = layer;
|
||||||
gnalog() << "... OK, need requantize\n";
|
gnalog() << "... OK, need requantize\n";
|
||||||
}, true, [&restartedLayer, &concatLayer, &concatIdxToUpdate](InferenceEngine::CNNLayer *from) {
|
}, true, [&restartedLayer, &concatLayer, &layerIdToUpdate](InferenceEngine::CNNLayer* from) {
|
||||||
// aborting UFS once found functional layer, and using only specified input of concat
|
// aborting UFS once found functional layer, and using only specified input of concat
|
||||||
return make_upstream_order(restartedLayer == nullptr ? from : nullptr,
|
return make_upstream_order(restartedLayer == nullptr ? from : nullptr,
|
||||||
from == concatLayer ? concatIdxToUpdate : -1);
|
from == concatLayer ? layerIdToUpdate : -1);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (restartedLayer == nullptr) {
|
if (restartedLayer == nullptr) {
|
||||||
THROW_GNA_EXCEPTION << "cannot requantize " << concatIdxToUpdate << "input to concat: " << concatLayer->name;
|
THROW_GNA_EXCEPTION << "cannot requantize " << layerIdToUpdate << "input to concat: " << concatLayer->name;
|
||||||
}
|
}
|
||||||
auto quantDataForConCatInput = InferenceEngine::getInjectedData<QuantizedLayerParams>(*restartedLayer);
|
auto quantDataForConCatInput = InferenceEngine::getInjectedData<QuantizedLayerParams>(*restartedLayer);
|
||||||
|
|
||||||
@ -495,6 +541,7 @@ class ScaleFactorPerLayer<InferenceEngine::ConcatLayer*> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
result = ScaleFactorUpdateResult(restartedLayer.get());
|
result = ScaleFactorUpdateResult(restartedLayer.get());
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -110,6 +110,12 @@ void GNAGraphCompiler::fillConcatConnections(InferenceEngine::CNNLayerPtr layer)
|
|||||||
InferenceEngine::details::product(begin(dataInput->getDims()),
|
InferenceEngine::details::product(begin(dataInput->getDims()),
|
||||||
end(dataInput->getDims())) * dataInput->getPrecision().size();
|
end(dataInput->getDims())) * dataInput->getPrecision().size();
|
||||||
|
|
||||||
|
// concat align layer can have additional padding, so the size of layer needs to be calculated
|
||||||
|
// based on original number of rows
|
||||||
|
if (ptrConcatLayerInput->CheckParamPresence("original_num_rows")) {
|
||||||
|
layer_size = ptrConcatLayerInput->GetParamAsInt("original_num_rows") * dataInput->getPrecision().size();
|
||||||
|
}
|
||||||
|
|
||||||
layerInfoItem.concatInputLayers.emplace_back(GNAConcatLayer::ConcatConnectedLayerInfo{ptrConcatLayerInput->name, concat_size, layer_size});
|
layerInfoItem.concatInputLayers.emplace_back(GNAConcatLayer::ConcatConnectedLayerInfo{ptrConcatLayerInput->name, concat_size, layer_size});
|
||||||
|
|
||||||
concat_size += layer_size;
|
concat_size += layer_size;
|
||||||
@ -848,7 +854,7 @@ void GNAGraphCompiler::CropPrimitive(InferenceEngine::CNNLayerPtr layer) {
|
|||||||
size_t cropOffset = offset.front() * cropLayer->precision.size();
|
size_t cropOffset = offset.front() * cropLayer->precision.size();
|
||||||
size_t cropOutputSize = dim.front() * cropLayer->precision.size();
|
size_t cropOutputSize = dim.front() * cropLayer->precision.size();
|
||||||
|
|
||||||
if (ALIGN64(cropOffset) == cropOffset) {
|
if (!LayerInfo(cropLayer).isCropAffined()) {
|
||||||
// leave crop as it is
|
// leave crop as it is
|
||||||
GNAPluginNS::GNACropLayer cropLayerInfoItem(layer);
|
GNAPluginNS::GNACropLayer cropLayerInfoItem(layer);
|
||||||
std::string& id = layer->name;
|
std::string& id = layer->name;
|
||||||
|
@ -260,10 +260,11 @@ class LayerInfo {
|
|||||||
bool isCropAffined() const noexcept {
|
bool isCropAffined() const noexcept {
|
||||||
auto cropLayer = dynamic_cast<InferenceEngine::CropLayer *> (layer);
|
auto cropLayer = dynamic_cast<InferenceEngine::CropLayer *> (layer);
|
||||||
if (cropLayer != nullptr && !cropLayer->offset.empty()) {
|
if (cropLayer != nullptr && !cropLayer->offset.empty()) {
|
||||||
try {
|
// currently crop layer only supports 2 bytes in int16 and int8 mode.
|
||||||
size_t cropOffset = cropLayer->offset.back() * cropLayer->precision.size();
|
// In fp32 mode this is not necessary but is useful for testing
|
||||||
|
auto bytesPerCropElement = 2;
|
||||||
|
size_t cropOffset = cropLayer->offset.back() * bytesPerCropElement;
|
||||||
return (ALIGN64(cropOffset) != cropOffset);
|
return (ALIGN64(cropOffset) != cropOffset);
|
||||||
} catch (InferenceEngine::details::InferenceEngineException) {}
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -703,15 +703,11 @@ void InsertCopyLayerPass::run() {
|
|||||||
if (LayerInfo(l).isConcat() && LayerInfo(prevIndirectLayer).isCrop()) { bInsert = true; }
|
if (LayerInfo(l).isConcat() && LayerInfo(prevIndirectLayer).isCrop()) { bInsert = true; }
|
||||||
|
|
||||||
if (bInsert) {
|
if (bInsert) {
|
||||||
if (LayerInfo(prevIndirectLayer).isCrop()) {
|
if (LayerInfo(prevIndirectLayer).isCropAffined()) {
|
||||||
auto cropLayer = LayerInfo(prevIndirectLayer).as<CropLayer *>();
|
|
||||||
size_t cropOffset = cropLayer->offset.back() * cropLayer->precision.size();
|
|
||||||
if (ALIGN(cropOffset, 8) != cropOffset) {
|
|
||||||
// The crop will be replaced by affine.
|
// The crop will be replaced by affine.
|
||||||
// Copy layer insertion is not required
|
// Copy layer insertion is not required
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
auto prevLayer = CNNNetPrevLayer(l, i);
|
auto prevLayer = CNNNetPrevLayer(l, i);
|
||||||
InsertCopyLayer(prevLayer, l, i, getPassManager());
|
InsertCopyLayer(prevLayer, l, i, getPassManager());
|
||||||
}
|
}
|
||||||
@ -788,8 +784,9 @@ void InsertConcatAligningFilterPass::run() {
|
|||||||
size_t num_rows_out = num_rows_padded + num_rows_in;
|
size_t num_rows_out = num_rows_padded + num_rows_in;
|
||||||
|
|
||||||
// encodes offset to beginning of split layer input
|
// encodes offset to beginning of split layer input
|
||||||
|
size_t bytesOffset = (aligned64_offset / bytesPerConcatElement) * (quantized ? bytesPerConcatElement : 4);
|
||||||
concatAligningFilter->params["output_offset"] =
|
concatAligningFilter->params["output_offset"] =
|
||||||
std::to_string((aligned64_offset / bytesPerConcatElement) * (quantized ? bytesPerConcatElement : 4));
|
std::to_string(bytesOffset);
|
||||||
|
|
||||||
// for padded rows we cannot use copy layer - TBD how to implement
|
// for padded rows we cannot use copy layer - TBD how to implement
|
||||||
concatAligningFilter->params["num_rows_padded"] = std::to_string(num_rows_padded);
|
concatAligningFilter->params["num_rows_padded"] = std::to_string(num_rows_padded);
|
||||||
@ -843,65 +840,72 @@ void ReorderConcatInputsPass::run() {
|
|||||||
}
|
}
|
||||||
int numOfLinkLayers = 0;
|
int numOfLinkLayers = 0;
|
||||||
|
|
||||||
for (auto & l : *pLayers) {
|
for (auto& l : *pLayers) {
|
||||||
// 1st stage locate concat align filter
|
// 1st stage locate concat
|
||||||
LayerInfo info(l);
|
LayerInfo info(l);
|
||||||
if (!info.isConcatAlignFilter()) {
|
if (!info.isConcat()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2rd locating concat
|
// 2nd stage locate first input in concat
|
||||||
if (l->outData.size() != 1) {
|
if (l->insData.size() < 2) {
|
||||||
THROW_GNA_EXCEPTION << "no concat layer after concat aligning layer" << l->name;
|
THROW_GNA_EXCEPTION << "Concat layer has unsupported number of incoming layers: " << l->name;
|
||||||
}
|
}
|
||||||
auto nextLayers = getInputTo(l->outData.front());
|
|
||||||
|
|
||||||
if (nextLayers.size() != 1) {
|
auto concatLayer = info.as<ConcatLayer*>();
|
||||||
THROW_GNA_EXCEPTION << "Invalid concat connection in align filter : " << l->name;
|
auto getLayerByIndex = [&concatLayer](int idx) {
|
||||||
|
auto input = concatLayer->insData[idx];
|
||||||
|
auto lockedInput = input.lock();
|
||||||
|
if (!lockedInput) {
|
||||||
|
THROW_GNA_EXCEPTION << "cannot get insdata : " << idx << " for layer: " << concatLayer->name;
|
||||||
}
|
}
|
||||||
auto concat = nextLayers.begin()->second;
|
return lockedInput;
|
||||||
if (!LayerInfo(concat).isConcat()) {
|
};
|
||||||
THROW_GNA_EXCEPTION << "no concat layer after concat-aligning layer" << l->name << ", but was: " << concat->type;
|
|
||||||
|
for (auto input_idx = 1; input_idx != concatLayer->insData.size(); input_idx++) {
|
||||||
|
auto concatInput = getLayerByIndex(input_idx);
|
||||||
|
auto currConcatLayer = getCreatorLayer(concatInput).lock();
|
||||||
|
|
||||||
|
LayerInfo infoConcatInput(currConcatLayer);
|
||||||
|
if (!infoConcatInput.isConcatAlignFilter()) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
// 3stage locate first input in concat
|
|
||||||
if (concat->insData.size() < 2) {
|
auto inputsToConcatPrev = CNNNetGetPrevLayersSkip(l, [](CNNLayerPtr origin) {
|
||||||
THROW_GNA_EXCEPTION << "Concat layer has unsupported number of incoming layers: " << concat->name;
|
|
||||||
}
|
|
||||||
auto inputsToConcatFirst = CNNNetGetPrevLayersSkip(concat, [](CNNLayerPtr origin){
|
|
||||||
return !LayerInfo(origin).isNonFunctional() && !LayerInfo(origin).isSplit();
|
return !LayerInfo(origin).isNonFunctional() && !LayerInfo(origin).isSplit();
|
||||||
}, 0);
|
}, input_idx - 1);
|
||||||
|
|
||||||
if (inputsToConcatFirst.empty()) {
|
if (inputsToConcatPrev.empty()) {
|
||||||
THROW_GNA_EXCEPTION << "cannot locate first input into concat layer: " << l;
|
THROW_GNA_EXCEPTION << "cannot locate first input into concat layer: " << currConcatLayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto firstInputToConcat = inputsToConcatFirst.front().first;
|
auto prevInputToConcat = inputsToConcatPrev.front().first;
|
||||||
|
|
||||||
// concat has first input of concat align filter - dont need to reorder it
|
// concat has first input of concat align filter - dont need to reorder it
|
||||||
if (firstInputToConcat == l) {
|
if (prevInputToConcat == currConcatLayer) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool bFinish = false;
|
bool bFinish = false;
|
||||||
// making a link activation possible without extra layer if first input to concat not a parent / indirect parent of second input
|
// making a link activation possible without extra layer if first input to concat not a parent / indirect parent of second input
|
||||||
// using ufs - upper first search
|
// using ufs - upper first search
|
||||||
gnalog() << "[UFS] searching for: "<< firstInputToConcat->name << "\n";
|
gnalog() << "[UFS] searching for: " << prevInputToConcat->name << "\n";
|
||||||
|
|
||||||
CNNNetDFS(l, [&l, &firstInputToConcat, &bFinish](CNNLayerPtr layer) {
|
CNNNetDFS(currConcatLayer, [&currConcatLayer, &prevInputToConcat, &bFinish](CNNLayerPtr layer) {
|
||||||
gnalog() << "[UFS] from : "<< l->name <<" reached: " << layer->name << "\n";
|
gnalog() << "[UFS] from : " << currConcatLayer->name << " reached: " << layer->name << "\n";
|
||||||
// found that direct input to concat is a indirect parent of align filter - so no link required
|
// found that direct input to concat is a indirect parent of align filter - so no link required
|
||||||
if (layer.get() == firstInputToConcat.get() || LayerInfo(firstInputToConcat).isInput()) {
|
if (layer.get() == prevInputToConcat.get() || LayerInfo(prevInputToConcat).isInput()) {
|
||||||
gnalog() << "[UFS] copy layer insertion needed\n";
|
gnalog() << "[UFS] copy layer insertion needed\n";
|
||||||
bFinish = true;
|
bFinish = true;
|
||||||
}
|
}
|
||||||
}, true, [&bFinish](InferenceEngine::CNNLayer* from) {
|
}, true, [&bFinish](InferenceEngine::CNNLayer* from) {
|
||||||
// aborting UFS once link not need
|
// aborting UFS once link not needed
|
||||||
return make_upstream_order(!bFinish ? from : nullptr);
|
return make_upstream_order(!bFinish ? from : nullptr);
|
||||||
});
|
});
|
||||||
|
|
||||||
auto linkName = std::string("link_") + std::to_string(numOfLinkLayers++);
|
auto linkName = std::string("link_") + std::to_string(numOfLinkLayers++);
|
||||||
|
|
||||||
auto linkWithoutQuant = std::make_shared<CNNLayer>(LayerParams({linkName, "link", Precision::FP32}));
|
auto linkWithoutQuant = std::make_shared<CNNLayer>(LayerParams({ linkName, "link", Precision::FP32 }));
|
||||||
|
|
||||||
auto link = quantized ?
|
auto link = quantized ?
|
||||||
InferenceEngine::injectData<QuantizedLayerParams>(linkWithoutQuant) :
|
InferenceEngine::injectData<QuantizedLayerParams>(linkWithoutQuant) :
|
||||||
@ -910,17 +914,18 @@ void ReorderConcatInputsPass::run() {
|
|||||||
|
|
||||||
auto linkOutData = std::make_shared<Data>(linkName,
|
auto linkOutData = std::make_shared<Data>(linkName,
|
||||||
TensorDesc(Precision::FP32,
|
TensorDesc(Precision::FP32,
|
||||||
SizeVector({1}),
|
SizeVector({ 1 }),
|
||||||
Layout::C));
|
Layout::C));
|
||||||
getCreatorLayer(linkOutData) = link;
|
getCreatorLayer(linkOutData) = link;
|
||||||
|
|
||||||
link->outData.push_back(linkOutData);
|
link->outData.push_back(linkOutData);
|
||||||
link->insData.push_back(l->outData.front());
|
link->insData.push_back(currConcatLayer->outData.front());
|
||||||
|
|
||||||
getInputTo(linkOutData)[firstInputToConcat->name + ".via.link"] = firstInputToConcat;
|
getInputTo(linkOutData)[prevInputToConcat->name + ".via.link"] = prevInputToConcat;
|
||||||
firstInputToConcat->insData.push_back(linkOutData);
|
prevInputToConcat->insData.push_back(linkOutData);
|
||||||
|
|
||||||
getInputTo(l->outData.front())[linkName] = link;
|
getInputTo(currConcatLayer->outData.front())[linkName] = link;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
// Copyright (C) 2020 Intel Corporation
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "subgraph_tests/concat_multi_input.hpp"
|
||||||
|
#include "common_test_utils/test_constants.hpp"
|
||||||
|
|
||||||
|
using namespace LayerTestsDefinitions;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
std::vector<std::vector<std::vector<size_t>>> inShapes = {
|
||||||
|
{{1, 8}, {1, 8}},
|
||||||
|
{{1, 3}, {1, 3}, {1, 3}},
|
||||||
|
{{1, 16}, {1, 16}, {1, 16}},
|
||||||
|
{{1, 16}, {1, 16}, {1, 16}, {1, 16}},
|
||||||
|
{{1, 32}, {1, 32}, {1, 32}, {1, 32}},
|
||||||
|
{{1, 16}, {1, 32}, {1, 16}, {1, 32}, {1, 16}, {1, 32}},
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::vector<InferenceEngine::Precision> netPrecisions = {
|
||||||
|
InferenceEngine::Precision::FP32,
|
||||||
|
InferenceEngine::Precision::FP16,
|
||||||
|
};
|
||||||
|
|
||||||
|
std::map<std::string, std::string> additional_config = {
|
||||||
|
{"GNA_DEVICE_MODE", "GNA_SW_EXACT"},
|
||||||
|
{"GNA_COMPACT_MODE", "NO"},
|
||||||
|
{"GNA_SCALE_FACTOR_0", "2048"},
|
||||||
|
{"GNA_PRECISION", "I16"},
|
||||||
|
};
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(concat_multi_input, ConcatMultiInput,
|
||||||
|
::testing::Combine(
|
||||||
|
::testing::ValuesIn(inShapes),
|
||||||
|
::testing::ValuesIn(netPrecisions),
|
||||||
|
::testing::Values(CommonTestUtils::DEVICE_GNA),
|
||||||
|
::testing::Values(additional_config)),
|
||||||
|
ConcatMultiInput::getTestCaseName);
|
||||||
|
|
||||||
|
} //namespace
|
@ -0,0 +1,34 @@
|
|||||||
|
// Copyright (C) 2020 Intel Corporation
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <tuple>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "functional_test_utils/layer_test_utils.hpp"
|
||||||
|
#include "ngraph_functions/utils/ngraph_helpers.hpp"
|
||||||
|
#include "ngraph_functions/builders.hpp"
|
||||||
|
|
||||||
|
typedef std::tuple<
|
||||||
|
std::vector<std::vector<size_t>>, // Input shapes
|
||||||
|
InferenceEngine::Precision, // Network Precision
|
||||||
|
std::string, // Target Device
|
||||||
|
std::map<std::string, std::string> // Config
|
||||||
|
> concatMultiParams;
|
||||||
|
|
||||||
|
namespace LayerTestsDefinitions {
|
||||||
|
|
||||||
|
class ConcatMultiInput : public testing::WithParamInterface<concatMultiParams>,
|
||||||
|
virtual public LayerTestsUtils::LayerTestsCommon {
|
||||||
|
public:
|
||||||
|
static std::string getTestCaseName(testing::TestParamInfo<concatMultiParams> obj);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void SetUp() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace LayerTestsDefinitions
|
@ -0,0 +1,88 @@
|
|||||||
|
// Copyright (C) 2020 Intel Corporation
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <tuple>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <ie_core.hpp>
|
||||||
|
|
||||||
|
#include "common_test_utils/common_utils.hpp"
|
||||||
|
#include "functional_test_utils/plugin_cache.hpp"
|
||||||
|
#include "functional_test_utils/layer_test_utils.hpp"
|
||||||
|
#include "functional_test_utils/blob_utils.hpp"
|
||||||
|
|
||||||
|
#include "ngraph_functions/pass/convert_prc.hpp"
|
||||||
|
|
||||||
|
#include "subgraph_tests/concat_multi_input.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
namespace LayerTestsDefinitions {
|
||||||
|
|
||||||
|
|
||||||
|
std::string ConcatMultiInput::getTestCaseName(testing::TestParamInfo<concatMultiParams> obj) {
|
||||||
|
std::vector<std::vector<size_t>> inputShapes;
|
||||||
|
InferenceEngine::Precision netPrecision;
|
||||||
|
std::string targetDevice;
|
||||||
|
std::map<std::string, std::string> additional_config;
|
||||||
|
std::tie(inputShapes, netPrecision, targetDevice, additional_config) = obj.param;
|
||||||
|
|
||||||
|
std::ostringstream result;
|
||||||
|
result << "IS=" << CommonTestUtils::vec2str(inputShapes) << "_";
|
||||||
|
result << "netPRC=" << netPrecision.name() << "_";
|
||||||
|
result << "targetDevice=" << targetDevice;
|
||||||
|
|
||||||
|
return result.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConcatMultiInput::SetUp() {
|
||||||
|
std::vector<std::vector<size_t>> inputShapes;
|
||||||
|
InferenceEngine::Precision netPrecision;
|
||||||
|
std::map<std::string, std::string> additional_config;
|
||||||
|
std::tie(inputShapes, netPrecision, targetDevice, additional_config) = this->GetParam();
|
||||||
|
configuration.insert(additional_config.begin(), additional_config.end());
|
||||||
|
|
||||||
|
auto ngPrc = FuncTestUtils::PrecisionUtils::convertIE2nGraphPrc(netPrecision);
|
||||||
|
std::vector<size_t> paramSize = { 1, 0 };
|
||||||
|
for (const auto& val : inputShapes) {
|
||||||
|
paramSize[1] += val[1];
|
||||||
|
}
|
||||||
|
auto params = ngraph::builder::makeParams(ngPrc, { paramSize });
|
||||||
|
auto stride = std::make_shared<ngraph::op::Constant>(ngraph::element::i64, ngraph::Shape{ 2 }, std::vector<int64_t>{ 1, 1 });
|
||||||
|
|
||||||
|
std::vector<int64_t> newAxis = { 0, 0 };
|
||||||
|
std::vector<int64_t> begin_mask = { 0, 0 };
|
||||||
|
std::vector<int64_t> end_mask = { 0, 0 };
|
||||||
|
std::vector<std::shared_ptr<ngraph::opset1::StridedSlice>> ssArray;
|
||||||
|
ngraph::OutputVector concatInput;
|
||||||
|
|
||||||
|
auto relu = std::make_shared<ngraph::opset1::Relu>(params[0]);
|
||||||
|
std::vector<int64_t> startOffset = { 0, 0 };
|
||||||
|
for (size_t i = 0; i < inputShapes.size(); ++i) {
|
||||||
|
std::vector<int64_t> shape = { static_cast<int64_t>(inputShapes[i][0]),
|
||||||
|
static_cast<int64_t>(inputShapes[i][1]) };
|
||||||
|
std::vector<int64_t> endoffset = { static_cast<int64_t>(inputShapes[i][0]) + startOffset[0],
|
||||||
|
static_cast<int64_t>(inputShapes[i][1]) + startOffset[1]};
|
||||||
|
auto begin = std::make_shared<ngraph::op::Constant>(ngraph::element::i64, ngraph::Shape{ 2 }, startOffset);
|
||||||
|
auto end = std::make_shared<ngraph::op::Constant>(ngraph::element::i64, ngraph::Shape{ 2 }, endoffset);
|
||||||
|
auto ss = std::make_shared<ngraph::opset1::StridedSlice>(relu, begin, end, stride, begin_mask, end_mask, newAxis);
|
||||||
|
ssArray.push_back(ss);
|
||||||
|
concatInput.push_back(ssArray[i]);
|
||||||
|
|
||||||
|
startOffset[1] += shape[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
auto concat = std::make_shared<ngraph::opset1::Concat>(concatInput, 1);
|
||||||
|
|
||||||
|
ngraph::ResultVector results{ std::make_shared<ngraph::opset1::Result>(concat) };
|
||||||
|
function = std::make_shared<ngraph::Function>(results, params, "ConcatMultiInput");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ConcatMultiInput, CompareWithRefImpl) {
|
||||||
|
Run();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace LayerTestsDefinitions
|
Loading…
Reference in New Issue
Block a user