16 #include <boost/numeric/conversion/cast.hpp> 17 #include <boost/assert.hpp> 18 #include <boost/format.hpp> 21 #include "caffe/proto/caffe.pb.h" 24 #include <google/protobuf/io/coded_stream.h> 25 #include <google/protobuf/io/zero_copy_stream.h> 26 #include <google/protobuf/io/zero_copy_stream_impl.h> 27 #include <google/protobuf/text_format.h> 28 #include <google/protobuf/stubs/common.h> 29 #include <google/protobuf/stubs/once.h> 30 #include <google/protobuf/io/coded_stream.h> 31 #include <google/protobuf/descriptor.h> 32 #include <google/protobuf/generated_message_reflection.h> 33 #include <google/protobuf/reflection_ops.h> 34 #include <google/protobuf/wire_format.h> 57 using namespace armnn;
58 using namespace caffe;
65 const float* GetArrayPtrFromBlob(
const LayerParameter& layerParam,
unsigned int blobIndex)
67 auto nBlobs = layerParam.blobs_size();
68 if (blobIndex >= boost::numeric_cast<unsigned int>(nBlobs))
73 "Expected data blob at index %1% in layer %2% not found. nBlobs=%2%. %4%") %
80 const BlobProto& blob = layerParam.blobs(boost::numeric_cast<int>(blobIndex));
82 const float* arrayPtr = blob.data().data();
86 void GetDataFromBlob(
const LayerParameter& layerParam, vector<float>& outData,
unsigned int blobIndex)
88 auto nBlobs = layerParam.blobs_size();
89 if (blobIndex >= boost::numeric_cast<unsigned int>(nBlobs))
94 "Expected data blob at index %1% in layer %2% not found. %3%") %
100 const BlobProto& blob = layerParam.blobs(boost::numeric_cast<int>(blobIndex));
103 if (blobSize != outData.size())
108 "Data blob at index %1% in layer %2% has an unexpected size. " 109 "Expected %3% elements but got %4% elements. %5%") %
118 for (
int i = 0; i < outSizeInt; ++i)
120 outData[
static_cast<size_t>(i)] = blob.data(i);
124 template <
typename T>
125 size_t SizeOfVectorData(
const vector<T>& vec)
127 return vec.size() *
sizeof(T);
130 void ValidateNumInputsOutputs(
const caffe::LayerParameter& layerParameter,
131 unsigned int numInputs,
132 unsigned int numOutputs)
134 int numInputsActual = layerParameter.bottom_size();
135 if (numInputs != boost::numeric_cast<unsigned int>(numInputsActual))
139 boost::format(
"Invalid number of inputs requested %1% for layer %2% " 140 "while only %3% present. %4%") %
142 layerParameter.name() %
147 int numOutputsActual = layerParameter.top_size();
148 if (numOutputs != boost::numeric_cast<unsigned int>(numOutputsActual))
152 boost::format(
"Invalid number of outputs requested %1% for layer %2% " 153 "while only %3% present. %4%") %
155 layerParameter.name() %
161 template <
typename ParamType,
typename ExtractOptional,
typename ExtractFallback,
typename ValueType>
162 ValueType GetOptionalWithFallback(
const ParamType& param,
163 ExtractOptional extractOptional,
164 ExtractFallback extractFallback,
165 ValueType defaultValue)
167 auto optValue = extractOptional(param, defaultValue);
170 return optValue.second;
172 auto fallbackValue = extractFallback(param, defaultValue);
173 return fallbackValue.second;
176 #define GET_OPTIONAL_WITH_VECTOR_FALLBACK(PARAM, \ 182 GetOptionalWithFallback( \ 184 [](const PARAM_TYPE & param, VALUE_TYPE defaultValue) \ 186 if (param.has_##OPTIONAL_VALUE ()) \ 188 return std::make_pair(true, param.OPTIONAL_VALUE ()); \ 192 return std::make_pair(false, defaultValue); \ 195 [](const PARAM_TYPE & param, VALUE_TYPE defaultValue) \ 197 if (param.FALLBACK_VECTOR##_size() > 0) \ 199 return std::make_pair(true, (param.FALLBACK_VECTOR ()).Get(0)); \ 203 return std::make_pair(false, defaultValue); \ 208 #define GET_OPTIONAL_WITH_FALLBACK(PARAM, \ 214 GetOptionalWithFallback( \ 216 [](const PARAM_TYPE & param, VALUE_TYPE defaultValue) \ 218 if (param.has_##OPTIONAL_VALUE ()) \ 220 return std::make_pair(true, param.OPTIONAL_VALUE ()); \ 224 return std::make_pair(false, defaultValue); \ 227 [](const PARAM_TYPE & param, VALUE_TYPE defaultValue) \ 229 if (param.has_##FALLBACK_VALUE ()) \ 231 return std::make_pair(true, param.FALLBACK_VALUE ()); \ 235 return std::make_pair(false, defaultValue); \ 242 const std::map<std::string, CaffeParserBase::OperationParsingFunction>
275 : m_Network(nullptr, nullptr)
297 const char* bindingPointDesc,
298 const std::unordered_map<std::string, BindingPointInfo>& nameToBindingInfo)
300 auto it = nameToBindingInfo.find(layerName);
301 if (it == nameToBindingInfo.end())
306 "Unknown binding %1% for layer '%2%'. %3%") %
316 std::vector<unsigned int> shape;
317 for (
int j = 0; j < blobShape.dim_size(); ++j)
319 shape.push_back(static_cast<unsigned int>(blobShape.dim(j)));
322 return TensorInfo(boost::numeric_cast<unsigned int>(shape.size()), shape.data(), DataType::Float32);
331 ret.set_dim(boost::numeric_cast<int>(i), desc.
GetShape()[i]);
341 std::vector<const caffe::LayerParameter*> ret;
342 ret.reserve(boost::numeric_cast<size_t>(layerParam.bottom_size()));
343 for (
int j = 0; j < layerParam.bottom_size(); ++j)
345 std::string inputName = layerParam.bottom(j);
352 "Can't find Caffe layer with top called '%1%', " 353 "which is listed as an input of '%2%'. %3%") %
358 ret.push_back(inputIt->second);
366 BOOST_ASSERT(layerParam.type() ==
"Input");
367 ValidateNumInputsOutputs(layerParam, 0, 1);
369 const InputParameter& param = layerParam.input_param();
379 const BlobShape* originalShape = param.shape_size() > 0 && param.shape(0).dim_size() > 0 ?
380 ¶m.shape(0) :
nullptr;
389 const TensorShape& overrideShape = overrideIt->second;
391 ( originalShape->dim(1) != overrideShape[1]
392 || originalShape->dim(2) != overrideShape[2]
393 || originalShape->dim(3) != overrideShape[3]))
398 "Parsed input shape for '%1%' is incompatible with the override provided. %2%") %
402 inputTensorInfo.
SetShape(overrideShape);
404 else if (!originalShape)
409 "No input descriptor given for '%1%' and no input shape found in caffe model. %2%") %
415 inputLayer->GetOutputSlot(0).SetTensorInfo(inputTensorInfo);
421 unsigned int kernelW,
422 unsigned int kernelH)
424 BOOST_ASSERT(layerParam.type() ==
"Convolution");
425 ValidateNumInputsOutputs(layerParam, 1, 1);
427 ConvolutionParameter convParam = layerParam.convolution_param();
429 const unsigned int numGroups = convParam.has_group() ? convParam.group() : 1;
432 BOOST_ASSERT(numGroups < inputShape.dim(1));
433 BOOST_ASSERT(numGroups > 1);
438 vector<string> convLayerNames(numGroups);
439 vector<armnn::IConnectableLayer*> convLayers(numGroups);
440 convLayerNames[0] = layerParam.name();
445 unsigned int splitterDimSizes[4] = {
static_cast<unsigned int>(inputShape.dim(0)),
446 static_cast<unsigned int>(inputShape.dim(1)),
447 static_cast<unsigned int>(inputShape.dim(2)),
448 static_cast<unsigned int>(inputShape.dim(3))};
453 splitterDimSizes[1] /= numGroups;
454 inputShape.set_dim(1, splitterDimSizes[1]);
460 for (
unsigned int g = 0; g < numGroups; ++g)
464 ss << layerParam.name() <<
"_" << g;
465 convLayerNames[g] = ss.str();
470 for (
unsigned int dimIdx=0; dimIdx < 4; dimIdx++)
472 splitterDesc.
SetViewSize(g, dimIdx, splitterDimSizes[dimIdx]);
476 const std::string splitterLayerName = std::string(
"splitter_") + layerParam.bottom(0);
485 unsigned int numFilters = convParam.num_output();
488 BlobShape outputShape;
489 outputShape.add_dim(0);
490 outputShape.set_dim(0, inputShape.dim(0));
491 outputShape.add_dim(1);
493 outputShape.set_dim(1, numFilters / numGroups);
494 outputShape.add_dim(2);
496 2, (static_cast<int>(
497 static_cast<float>(inputShape.dim(2) + 2 * desc.
m_PadBottom - kernelH) /
498 static_cast<float>(desc.
m_StrideY)) + 1));
499 outputShape.add_dim(3);
501 3, (static_cast<int>(
502 static_cast<float>(inputShape.dim(3) + 2 * desc.
m_PadRight - kernelW) /
503 static_cast<float>(desc.
m_StrideX)) + 1));
506 vector<float> weightData(boost::numeric_cast<size_t>(numGroups *
511 GetDataFromBlob(layerParam, weightData, 0);
513 const unsigned int weightDimSizes[4] = {
514 static_cast<unsigned int>(outputShape.dim(1)),
515 static_cast<unsigned int>(inputShape.dim(1)),
520 vector<float> biasData;
524 biasData.resize(boost::numeric_cast<size_t>(numGroups * outputShape.dim(1)), 1.f);
525 GetDataFromBlob(layerParam, biasData, 1);
527 const unsigned int biasDimSizes[1] = {
static_cast<unsigned int>(outputShape.dim(1))};
528 biasInfo =
TensorInfo(1, biasDimSizes, DataType::Float32);
531 const unsigned int numWeightsPerGroup =
boost::numeric_cast<
unsigned int>(weightData.size()) / numGroups;
532 const unsigned int numBiasesPerGroup =
boost::numeric_cast<
unsigned int>(biasData.size()) / numGroups;
534 for (
unsigned int g = 0; g < numGroups; ++g)
541 weightData.data() + numWeightsPerGroup * g);
548 ConstTensor biases(biasInfo, biasData.data() + numBiasesPerGroup * g);
551 convLayer =
m_Network->AddConvolution2dLayer(desc,
554 convLayerNames[g].c_str());
555 convLayers[g] = convLayer;
560 splitterLayer ? splitterLayer->
GetOutputSlot(g) : inputConnection;
561 splitterInputConnection.
Connect(convLayer->GetInputSlot(0));
568 unsigned int concatDimSizes[4] = {
static_cast<unsigned int>(outputShape.dim(0)),
569 static_cast<unsigned int>(outputShape.dim(1)),
570 static_cast<unsigned int>(outputShape.dim(2)),
571 static_cast<unsigned int>(outputShape.dim(3))};
578 for (
unsigned int g = 0; g < numGroups; ++g)
584 concatDimSizes[1] *= numGroups;
585 outputShape.set_dim(1, concatDimSizes[1]);
595 "Failed to create final concat layer for Split+Convolution+Concat. " 596 "Layer=%1% #groups=%2% #filters=%3% %4%") %
603 for (
unsigned int g = 0; g < numGroups; ++g)
605 convLayers[g]->GetOutputSlot(0).Connect(concatLayer->
GetInputSlot(g));
613 unsigned int kernelW,
614 unsigned int kernelH)
616 BOOST_ASSERT(layerParam.type() ==
"Convolution");
617 ValidateNumInputsOutputs(layerParam, 1, 1);
619 ConvolutionParameter convParam = layerParam.convolution_param();
631 unsigned int numFilters = convParam.num_output();
633 BlobShape outputShape;
634 outputShape.add_dim(0);
635 outputShape.set_dim(0, inputShape.dim(0));
636 outputShape.add_dim(1);
637 outputShape.set_dim(1, numFilters);
638 outputShape.add_dim(2);
640 2, (static_cast<int>(
641 static_cast<float>(inputShape.dim(2) + 2 * desc.m_PadBottom - kernelH) /
642 static_cast<float>(desc.m_StrideY)) + 1));
643 outputShape.add_dim(3);
645 3, (static_cast<int>(
646 static_cast<float>(inputShape.dim(3) + 2 * desc.m_PadRight - kernelW) /
647 static_cast<float>(desc.m_StrideX)) + 1));
650 size_t allWeightsSize =
boost::numeric_cast<
size_t>(inputShape.dim(1) * kernelH * kernelW);
651 vector<float> weightData(allWeightsSize);
653 GetDataFromBlob(layerParam, weightData, 0);
656 const unsigned int weightDimSizes[4] = {
657 static_cast<unsigned int>(1),
658 static_cast<unsigned int>(inputShape.dim(1)),
665 vector<float> biasData;
666 if (desc.m_BiasEnabled)
670 biasData.resize(boost::numeric_cast<size_t>(outputShape.dim(1)), 1.f);
671 GetDataFromBlob(layerParam, biasData, 1);
673 const unsigned int biasDimSizes[1] = {
static_cast<unsigned int>(outputShape.dim(1))};
674 biasInfo =
TensorInfo(1, biasDimSizes, DataType::Float32);
679 returnLayer =
m_Network->AddDepthwiseConvolution2dLayer(desc,
682 layerParam.name().c_str());
689 "Failed to create depthwise convolution layer. " 690 "Layer=%1% #filters=%2% %3%") %
696 inputConnection.
Connect(returnLayer->GetInputSlot(0));
714 BOOST_ASSERT(layerParam.type() ==
"Convolution");
715 ValidateNumInputsOutputs(layerParam, 1, 1);
717 ConvolutionParameter convParam = layerParam.convolution_param();
719 const unsigned int numGroups = convParam.has_group() ? convParam.group() : 1;
720 unsigned int numFilters = convParam.num_output();
722 const auto notFound = std::numeric_limits<unsigned int>::max();
725 kernel_h, kernel_size,
unsigned int, notFound);
727 kernel_w, kernel_size,
unsigned int, notFound);
730 stride_h, stride,
unsigned int, 1u);
732 stride_w, stride,
unsigned int, 1u);
735 pad_h, pad,
unsigned int, 0u);
737 pad_w, pad,
unsigned int, 0u);
740 convolution2dDescriptor.
m_PadLeft = padW;
742 convolution2dDescriptor.
m_PadTop = padH;
744 convolution2dDescriptor.
m_StrideX = strideW;
745 convolution2dDescriptor.
m_StrideY = strideH;
746 convolution2dDescriptor.
m_BiasEnabled = convParam.has_bias_term() ? convParam.bias_term() :
true;
748 if (numGroups > numFilters)
753 "Error parsing Convolution: %1%. " 754 "The 'group'=%2% parameter cannot be larger than the " 755 "number of filters supplied ='%3%'. %4%") %
762 if (inputShape.dim_size() != 4)
767 "Convolution input shape is expected to have 4 dimensions. " 768 "%1%'s input has only %2%. %3%") %
770 inputShape.dim_size() %
776 if (numGroups > inputShape.dim(1))
781 "Error parsing Convolution: %1%. " 782 "The 'group'=%2% parameter cannot be larger than the " 783 "channel of the input shape=%3% (in NCHW format). %4%") %
789 else if (numGroups == inputShape.dim(1))
809 BlobShape outputShape;
810 outputShape.add_dim(0);
811 outputShape.set_dim(0, inputShape.dim(0));
812 outputShape.add_dim(1);
813 outputShape.set_dim(1, numFilters);
814 outputShape.add_dim(2);
816 2, (static_cast<int>(
817 static_cast<float>(inputShape.dim(2) + 2 * padH - kernelH) /
818 static_cast<float>(strideH)) + 1));
819 outputShape.add_dim(3);
821 3, (static_cast<int>(
822 static_cast<float>(inputShape.dim(3) + 2 * padW - kernelW) /
823 static_cast<float>(strideW)) + 1));
826 vector<float> weightData(boost::numeric_cast<size_t>(inputShape.dim(1) *
830 GetDataFromBlob(layerParam, weightData, 0);
832 const unsigned int weightDimSizes[4] = {
833 static_cast<unsigned int>(outputShape.dim(1)),
834 static_cast<unsigned int>(inputShape.dim(1)),
843 vector<float> biasData;
848 biasData.resize(boost::numeric_cast<size_t>(outputShape.dim(1)), 1.f);
849 GetDataFromBlob(layerParam, biasData, 1);
851 const unsigned int biasDimSizes[1] = {
static_cast<unsigned int>(outputShape.dim(1))};
852 biasInfo =
TensorInfo(1, biasDimSizes, DataType::Float32);
858 returnLayer =
m_Network->AddConvolution2dLayer(convolution2dDescriptor,
861 layerParam.name().c_str());
864 inputConnection.
Connect(returnLayer->GetInputSlot(0));
872 "Failed to create Convolution layer. " 873 "Layer=%1% #groups=%2% #filters=%3% %4%") %
889 ValidateNumInputsOutputs(layerParam, 1, 1);
890 PoolingParameter param = layerParam.pooling_param();
893 const auto notFound = std::numeric_limits<unsigned int>::max();
896 kernel_h, kernel_size,
unsigned int, notFound);
898 kernel_w, kernel_size,
unsigned int, notFound);
900 if ((kernel_h == notFound || kernel_w == notFound) && param.has_global_pooling())
907 stride_h, stride,
unsigned int, notFound);
909 stride_h, stride,
unsigned int, notFound);
911 if ((stride_h == notFound || stride_w == notFound) && param.has_global_pooling())
918 pad_h, pad,
unsigned int, 0u);
920 pad_w, pad,
unsigned int, 0u);
924 if (param.has_pool())
926 PoolingParameter_PoolMethod p = param.pool();
929 case PoolingParameter_PoolMethod_MAX:
934 case PoolingParameter_PoolMethod_AVE:
936 pooling2dDescriptor.
m_PoolType = PoolingAlgorithm::Average;
939 case PoolingParameter_PoolMethod_STOCHASTIC:
944 "Pooling Layer: Stochastic Pooling Not Supported. Layer=%1% %2%") %
953 "Pooling Layer: unknown pooling method: %1% for layer: %2% %3%") %
965 "No Pooling Method Defined for %1% %2%") %
972 pooling2dDescriptor.
m_PadTop = pad_h;
974 pooling2dDescriptor.
m_StrideX = stride_w;
975 pooling2dDescriptor.
m_StrideY = stride_h;
983 layerParam.name().c_str());
988 static_cast<unsigned int>(ceil(
989 static_cast<float>(inputInfo.
GetShape()[2] + 2 * pad_h - kernel_h) /
990 boost::numeric_cast<float>(stride_h))) + 1,
991 static_cast<unsigned int>(ceil(
992 static_cast<float>(inputInfo.
GetShape()[3] + 2 * pad_w - kernel_w) /
993 boost::numeric_cast<float>(stride_w))) + 1 },
1003 ValidateNumInputsOutputs(layerParam, 1, 1);
1005 const string& name = layerParam.name();
1006 const ReLUParameter& param = layerParam.relu_param();
1009 const float negativeSlope = param.negative_slope();
1010 if (negativeSlope == 0.0f)
1012 activationDescriptor.
m_Function = ActivationFunction::ReLu;
1016 activationDescriptor.
m_Function = ActivationFunction::LeakyReLu;
1017 activationDescriptor.
m_A = negativeSlope;
1029 ValidateNumInputsOutputs(layerParam, 1, 1);
1031 LRNParameter param = layerParam.lrn_param();
1041 if (param.has_norm_region())
1043 LRNParameter_NormRegion n = param.norm_region();
1046 case LRNParameter_NormRegion_ACROSS_CHANNELS:
1048 normalizationDescriptor.
m_NormChannelType = NormalizationAlgorithmChannel::Across;
1051 case LRNParameter_NormRegion_WITHIN_CHANNEL:
1053 normalizationDescriptor.
m_NormChannelType = NormalizationAlgorithmChannel::Within;
1061 "Unknown region %1% for LRN layer %2% %3%") %
1071 normalizationDescriptor.
m_NormChannelType = NormalizationAlgorithmChannel::Across;
1074 normalizationDescriptor.
m_NormMethodType = NormalizationAlgorithmMethod::LocalBrightness;
1075 if (param.has_local_size())
1077 normalizationDescriptor.
m_NormSize = param.local_size();
1084 "local_size not defined for LRN layer %1% %2%") %
1089 if (param.has_alpha())
1091 normalizationDescriptor.
m_Alpha = param.alpha();
1099 "Alpha not defined for LRN layer %1% %2%") %
1103 if (param.has_beta())
1105 normalizationDescriptor.
m_Beta = param.beta();
1112 "Beta not defined for LRN layer %1% %2%") %
1119 normalizationDescriptor.
m_K = param.k();
1123 normalizationDescriptor.
m_K = 1;
1127 layerParam.name().c_str());
1136 InnerProductParameter param = layerParam.inner_product_param();
1138 ValidateNumInputsOutputs(layerParam, 1, 1);
1140 unsigned int outputSize = param.num_output();
1150 if (param.has_transpose())
1167 unsigned int inputSize = inputInfo.
GetShape()[1];
1170 inputSize *= inputInfo.
GetShape()[i];
1173 const float* weightDataPtr = GetArrayPtrFromBlob(layerParam, 0);
1174 const unsigned int swTD[2] = { outputSize, inputSize };
1183 const float* biasDataPtr = GetArrayPtrFromBlob(layerParam, 1);
1185 const unsigned int sbTD[1] = { outputSize };
1189 fullyConnectedLayer =
m_Network->AddFullyConnectedLayer(tensorFullyConnectedDescriptor,
1192 layerParam.name().c_str());
1196 fullyConnectedLayer =
m_Network->AddFullyConnectedLayer(tensorFullyConnectedDescriptor,
1199 layerParam.name().c_str());
1210 ValidateNumInputsOutputs(layerParam, 1, 1);
1212 SoftmaxParameter param = layerParam.softmax_param();
1221 softmaxDescriptor.
m_Axis = 1;
1224 layerParam.name().c_str());
1232 ValidateNumInputsOutputs(layerParam, 2, 1);
1239 EltwiseParameter_EltwiseOp operation = EltwiseParameter_EltwiseOp_SUM;
1241 if (layerParam.has_eltwise_param() && layerParam.eltwise_param().has_operation())
1243 operation = layerParam.eltwise_param().operation();
1249 case EltwiseParameter_EltwiseOp_SUM:
1251 newLayer =
m_Network->AddAdditionLayer(layerParam.name().c_str());
1254 case EltwiseParameter_EltwiseOp_PROD:
1256 newLayer =
m_Network->AddMultiplicationLayer(layerParam.name().c_str());
1264 "Unsupported operation %1% in Eltwise layer %2% %3%") %
1279 unsigned int numInputs =
static_cast<unsigned int>(layerParam.bottom_size());
1281 unsigned int concatDim = 1;
1282 unsigned int numOfDims = 4;
1285 OriginsDescriptor concatDescriptor(static_cast<uint32_t>(numInputs), numOfDims);
1286 std::vector<unsigned int>mergeDimSizes(numOfDims, 0u);
1288 unsigned int mergeDim = 0;
1289 for (
unsigned int viewIndex = 0; viewIndex < numInputs; ++viewIndex)
1292 layerParam.bottom(boost::numeric_cast<int>(viewIndex))).
GetTensorInfo();
1299 "The number of dimensions for input tensors of " 1300 "the concatenation op should be 4. Inputs of %1% has " 1301 "%2% dimensions. %3%") %
1307 mergeDimSizes[0] = inputInfo.
GetShape()[0];
1308 mergeDimSizes[1] = inputInfo.
GetShape()[1];
1309 mergeDimSizes[2] = inputInfo.
GetShape()[2];
1310 mergeDimSizes[3] = inputInfo.
GetShape()[3];
1312 for (
unsigned int j = 0; j < concatDim; ++j)
1318 mergeDim += mergeDimSizes[concatDim];
1320 for (
unsigned int j = concatDim+1; j < numOfDims; ++j)
1325 mergeDimSizes[concatDim] = mergeDim;
1328 for (
unsigned int i = 0; i < numInputs; ++i)
1340 ValidateNumInputsOutputs(layerParam, 1, 1);
1344 string name = layerParam.name();
1346 BatchNormParameter param = layerParam.batch_norm_param();
1349 if (param.has_use_global_stats())
1351 if (!param.use_global_stats())
1356 "Error parsing Batch Norm layer '%1%': " 1357 "Parameter 'use_global_stats' is set to false, which is " 1358 "unsupported (value used for training). %2%") %
1365 desc.
m_Eps = param.eps();
1367 unsigned int channels = inputInfo.
GetShape()[1];
1368 unsigned int shape[] = {channels};
1370 vector<float> meanData(channels);
1371 GetDataFromBlob(layerParam, meanData, 0);
1373 vector<float> varianceData(channels);
1374 GetDataFromBlob(layerParam, varianceData, 1);
1377 const BlobProto& blob = layerParam.blobs(boost::numeric_cast<int>(2));
1378 const float movingAverageFactor = blob.data(boost::numeric_cast<int>(0));
1379 if(movingAverageFactor != 0.0f)
1381 const float scaleFactor = 1.0f / movingAverageFactor;
1382 auto scaleFunction = [scaleFactor](
float f) ->
float {
return f * scaleFactor; };
1384 std::transform(varianceData.begin(), varianceData.end(), varianceData.begin(), scaleFunction);
1385 std::transform(meanData.begin(), meanData.end(), meanData.begin(), scaleFunction);
1389 vector<float> betaData(channels, 0.0f);
1390 vector<float> gammaData(channels, 1.0f);
1398 mean, variance, beta, gamma, name.c_str());
1407 ValidateNumInputsOutputs(layerParam, 1, 1);
1411 string name = layerParam.name();
1413 ScaleParameter param = layerParam.scale_param();
1414 if (param.axis() != 1)
1420 "Loading Scale Layer: Only axis 1 is supported currently. " 1421 "Layer=%1% Axis=%2% %3%") %
1427 unsigned int channels = inputInfo.
GetShape()[1];
1428 unsigned int shape[] = {channels};
1432 vector<float> meanData(channels, 0.0f);
1433 vector<float> varianceData(channels, 1.0f);
1434 vector<float> betaData(channels, 0.0f);
1435 vector<float> gammaData(channels);
1437 GetDataFromBlob(layerParam, gammaData, 0);
1439 if(param.has_bias_term())
1441 GetDataFromBlob(layerParam, betaData, 1);
1450 mean, variance, beta, gamma, name.c_str());
1459 if (layerParam.bottom_size() != 1)
1464 "Split layer '%1%' should have exactly 1 bottom. " 1465 "#bottoms=%2% %3%") %
1467 layerParam.bottom_size() %
1471 for (
int i = 0; i < layerParam.top_size(); i++)
1480 if (layerParam.bottom_size() != 1 || layerParam.top_size() != 1)
1485 "Dropout layer '%1%' should have exactly 1 bottom and 1 top. " 1486 "#bottoms=%2% #tops=%3% %4%") %
1488 layerParam.bottom_size() %
1489 layerParam.top_size() %
1512 const char* bindingPointDesc,
1513 std::unordered_map<std::string, BindingPointInfo>& nameToBindingInfo)
1515 const std::string layerName = layer->
GetName();
1516 auto it = nameToBindingInfo.find(layerName);
1517 if (it == nameToBindingInfo.end())
1519 nameToBindingInfo[layerName] = std::make_pair(
id, tensorInfo);
1526 "Id %1% used by more than one %2% layer %3%") %
1545 "Could not find armnn output slot for Caffe top '%1%' %2%") %
1564 "Attempting to add duplicate entry for Caffe top '%1%' %2%") %
1575 std::map<std::string, std::vector<caffe::LayerParameter*>> layersByTop;
1576 for (
int layerIdx = 0; layerIdx < netParameter.layer_size(); ++layerIdx)
1578 caffe::LayerParameter& layer = *netParameter.mutable_layer(layerIdx);
1579 std::string name = layer.name();
1580 for (
int i = 0; i < layer.top_size(); ++i)
1582 layersByTop[layer.top(i)].push_back(&layer);
1588 for (
auto layersWithSameTopIt : layersByTop)
1590 const std::string& top = layersWithSameTopIt.first;
1591 const std::vector<caffe::LayerParameter*>& layersWithSameTop = layersWithSameTopIt.second;
1595 for (
unsigned int layerIdx = 0; layerIdx < layersWithSameTop.size() - 1; ++layerIdx)
1597 caffe::LayerParameter& layer1 = *layersWithSameTop[layerIdx];
1598 caffe::LayerParameter& layer2 = *layersWithSameTop[layerIdx+1];
1599 if (layer1.top_size() != 1)
1604 "Node '%1%' is an in-place layer but doesn't have exactly one " 1605 "top. It has %2% instead. %3%") %
1610 std::string newTop = layer1.name() +
"_top";
1611 layer1.set_top(0, newTop);
1612 if (layer2.bottom_size() != 1 || layer2.bottom(0) != top)
1617 "Node '%1%' is an in-place layer but " 1618 "doesn't have exactly one bottom, or it doesn't match its top. " 1619 "#bottoms=%2%, first bottom is %3%, top is %4% %5%") %
1625 layer2.set_bottom(0, newTop);
1636 if (netParameter.input_size() > 0)
1638 LayerParameter* newLayer = netParameter.add_layer();
1640 newLayer->set_type(
"Input");
1641 newLayer->set_name(netParameter.input(0));
1642 newLayer->add_top(netParameter.input(0));
1644 InputParameter* inputParam = newLayer->mutable_input_param();
1645 BlobShape* shape = inputParam->add_shape();
1647 int dim_size = netParameter.input_dim_size();
1648 for (
int i = 0; i < dim_size; ++i)
1650 shape->add_dim(netParameter.input_dim(i));
1658 for (
int i = 0; i < netParameter.layer_size(); ++i)
1660 const caffe::LayerParameter& layer = netParameter.layer(i);
1661 for (
int i = 0; i < layer.top_size(); ++i)
1668 std::vector<const caffe::LayerParameter*> targetLayers;
1677 "Couldn't find requested output layer '%1%' in graph %2%") %
1678 requestedOutputName %
1681 targetLayers.push_back(nodeIt->second);
1685 std::vector<const caffe::LayerParameter*> sortedNodes;
1686 if (!armnnUtils::GraphTopologicalSort<const caffe::LayerParameter*>(
1688 [
this](
const caffe::LayerParameter* node)
1697 "Cycle detected in graph. #nodes: %1% %2%") %
1698 sortedNodes.size() %
1703 for (
const caffe::LayerParameter* current : sortedNodes)
1710 boost::format(
"Unsupported layer type: '%1%' for layer %2% %3%") %
1715 auto func = it->second;
1716 (this->*func)(*current);
1720 for (
const std::string& requestedOutput : m_RequestedOutputs)
1727 outputSlot.
Connect(outputLayer->GetInputSlot(0));
1729 TrackOutputBinding(outputLayer, outputId, outputLayer->GetInputSlot(0).GetConnection()->GetTensorInfo());
1734 const std::map<std::string, armnn::TensorShape>& inputShapes,
1735 const std::vector<std::string>& requestedOutputs)
1737 FILE* fd = fopen(graphFile,
"r");
1744 "Failed to open graph file: %1% %2%") %
1750 NetParameter netParam;
1751 auto input =
new google::protobuf::io::FileInputStream(fileno(fd));
1752 bool success = google::protobuf::TextFormat::Parse(input, &netParam);
1761 "Failed to parse graph file: %1% %2%") %
1770 const std::map<std::string, armnn::TensorShape>& inputShapes,
1771 const std::vector<std::string>& requestedOutputs)
1774 NetParameter netParam;
1775 bool success = google::protobuf::TextFormat::ParseFromString(protoText, &netParam);
1782 "Failed to parse graph string %1%") %
1790 const std::map<std::string, armnn::TensorShape>& inputShapes,
1791 const std::vector<std::string>& requestedOutputs)
1793 FILE* fd = fopen(graphFile,
"rb");
1800 "Failed to open graph file at: %1% %2%") %
1806 NetParameter netParam;
1808 FileInputStream inStream(fileno(fd));
1809 CodedInputStream codedStream(&inStream);
1810 codedStream.SetTotalBytesLimit(INT_MAX, INT_MAX);
1811 bool success = netParam.ParseFromCodedStream(&codedStream);
1819 "Failed to parse protobuf file: %1% %2%") %
1830 const std::map<std::string, armnn::TensorShape>& inputShapes,
1831 const std::vector<std::string>& requestedOutputs)
1839 if (requestedOutputs.size() == 0)
1841 throw ParseException(
"requestedOutputs must have at least one entry");
uint32_t m_PadBottom
Padding bottom value in the height dimension.
bool m_BiasEnabled
Enable/disable bias.
virtual unsigned int GetNumOutputSlots() const =0
Returns the number of connectable output slots.
A ViewsDescriptor for the SplitterLayer.
Interface for a layer that is connectable to other layers via InputSlots and OutputSlots.
void ParseConvLayer(const caffe::LayerParameter &layerParam)
uint32_t m_PadBottom
Padding bottom value in the height dimension.
virtual BindingPointInfo GetNetworkInputBindingInfo(const std::string &name) const override
Retrieves binding info (layer id and tensor info) for the network input identified by the given layer...
float m_K
Kappa value used for the across channel normalization equation.
int m_Axis
Scalar, defaulted to the last index (-1), specifying the dimension the activation will be performed o...
const TensorShape & GetShape() const
static void Destroy(ICaffeParser *parser)
uint32_t m_PadLeft
Padding left value in the width dimension.
armnn::IOutputSlot & GetArmnnOutputSlotForCaffeTop(const std::string &caffeTopName) const
Retrieves the Armnn IOutputSlot representing the given Caffe top.
virtual armnn::INetworkPtr CreateNetworkFromString(const char *protoText, const std::map< std::string, armnn::TensorShape > &inputShapes, const std::vector< std::string > &requestedOutputs) override
Creates the network directly from protobuf text in a string. Useful for debugging/testing.
bool m_TransposeWeightMatrix
Enable/disable transpose weight matrix.
uint32_t m_PoolWidth
Pooling width value.
A Convolution2dDescriptor for the Convolution2dLayer.
float m_Alpha
Alpha value for the normalization equation.
uint32_t m_PadLeft
Padding left value in the width dimension.
static const std::map< std::string, OperationParsingFunction > ms_CaffeLayerNameToParsingFunctions
Maps Caffe layer names to parsing member functions.
float m_Eps
Value to add to the variance. Used to avoid dividing by zero.
PaddingMethod m_PaddingMethod
The padding method to be used. (Exclude, IgnoreValue).
void LoadNetParam(caffe::NetParameter &netParameter)
does the actual conversion from caffe::NetParameter to armnn::INetwork
void ParseInputLayer(const caffe::LayerParameter &layerParam)
Adds an armnn layer to m_Network given a Caffe LayerParameter of the correct type and is responsible ...
uint32_t m_PadTop
Padding top value in the height dimension.
uint32_t m_PadRight
Padding right value in the width dimension.
Copyright (c) 2020 ARM Limited.
static std::pair< armnn::LayerBindingId, armnn::TensorInfo > GetBindingInfo(const std::string &layerName, const char *bindingPointDesc, const std::unordered_map< std::string, BindingPointInfo > &bindingInfos)
std::vector< std::string > m_RequestedOutputs
Caffe networks are loaded from protobuf files (binary or text) using the protobuf library and the gen...
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
int LayerBindingId
Type of identifiers for bindable layers (inputs, outputs).
virtual void SetTensorInfo(const TensorInfo &tensorInfo)=0
NormalizationAlgorithmMethod m_NormMethodType
Normalization method algorithm to use (LocalBrightness, LocalContrast).
virtual armnn::INetworkPtr CreateNetworkFromTextFile(const char *graphFile, const std::map< std::string, armnn::TensorShape > &inputShapes, const std::vector< std::string > &requestedOutputs) override
Create the network from a protobuf text file on disk.
void SetShape(const TensorShape &newShape)
void ParseBatchNormLayer(const caffe::LayerParameter &layerParam)
void TrackOutputBinding(armnn::IConnectableLayer *layer, armnn::LayerBindingId id, const armnn::TensorInfo &tensorInfo)
uint32_t m_PoolHeight
Pooling height value.
uint32_t m_PadTop
Padding top value in the height dimension.
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
void ResolveInPlaceLayers(caffe::NetParameter &netParameter)
Modifies the Caffe network to replace "in-place" layers (whose top() and bottom() are both the same) ...
uint32_t m_PadRight
Padding right value in the width dimension.
Status SetViewSize(uint32_t view, uint32_t coord, uint32_t value)
Set the size of the views.
static ICaffeParserPtr Create()
An output connection slot for a layer.
An OriginsDescriptor for the ConcatLayer.
std::unordered_map< std::string, armnn::IOutputSlot * > m_ArmnnOutputSlotForCaffeTop
As we add armnn layers we store the armnn IOutputSlot which corresponds to the Caffe tops...
A FullyConnectedDescriptor for the FullyConnectedLayer.
bool m_BiasEnabled
Enable/disable bias.
A tensor defined by a TensorInfo (shape and data type) and an immutable backing store.
std::unordered_map< std::string, BindingPointInfo > m_NetworkInputsBindingInfo
maps input layer names to their corresponding ids and tensor infos
virtual armnn::INetworkPtr CreateNetworkFromBinaryFile(const char *graphFile, const std::map< std::string, armnn::TensorShape > &inputShapes, const std::vector< std::string > &requestedOutputs) override
Create the network from a protobuf binary file on disk.
#define GET_OPTIONAL_WITH_FALLBACK(PARAM, PARAM_TYPE, OPTIONAL_VALUE, FALLBACK_VALUE, VALUE_TYPE, DEFAULT_VALUE)
std::enable_if_t< std::is_unsigned< Source >::value &&std::is_unsigned< Dest >::value, Dest > numeric_cast(Source source)
std::unordered_map< std::string, BindingPointInfo > m_NetworkOutputsBindingInfo
maps output layer names to their corresponding ids and tensor infos
std::unique_ptr< ICaffeParser, void(*)(ICaffeParser *parser)> ICaffeParserPtr
armnn::TensorInfo BlobShapeToTensorInfo(const caffe::BlobShape &blobShape) const
Converts Caffe's protobuf tensor shape format to ArmNN's.
std::map< std::string, armnn::TensorShape > m_InputShapes
An ActivationDescriptor for the ActivationLayer.
void ParseInnerProductLayer(const caffe::LayerParameter &layerParam)
void ParseSoftmaxLayer(const caffe::LayerParameter &layerParam)
armnn::INetworkPtr CreateNetworkFromNetParameter(caffe::NetParameter &netParam, const std::map< std::string, armnn::TensorShape > &inputShapes, const std::vector< std::string > &requestedOutputs)
Parses a NetParameter loaded into memory from one of the other CreateNetwork*.
void ParseReluLayer(const caffe::LayerParameter &layerParam)
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
void AddConvLayerWithSplits(const caffe::LayerParameter &layerParam, const armnn::Convolution2dDescriptor &desc, unsigned int kernelW, unsigned int kernelH)
ParseConv may use these helpers depending on the group parameter.
static void TrackBindingPoint(armnn::IConnectableLayer *layer, armnn::LayerBindingId id, const armnn::TensorInfo &tensorInfo, const char *bindingPointDesc, std::unordered_map< std::string, BindingPointInfo > &nameToBindingInfo)
void ParseScaleLayer(const caffe::LayerParameter &layerParam)
void ParseDropoutLayer(const caffe::LayerParameter &layerParam)
NormalizationAlgorithmChannel m_NormChannelType
Normalization channel algorithm to use (Across, Within).
float m_A
Alpha upper bound value used by the activation functions. (BoundedReLu, Linear, TanH).
virtual BindingPointInfo GetNetworkOutputBindingInfo(const std::string &name) const override
Retrieves binding info (layer id and tensor info) for the network output identified by the given laye...
EmptyOptional is used to initialize the Optional class in case we want to have default value for an O...
std::pair< armnn::LayerBindingId, armnn::TensorInfo > BindingPointInfo
void TrackInputBinding(armnn::IConnectableLayer *layer, armnn::LayerBindingId id, const armnn::TensorInfo &tensorInfo)
PoolingAlgorithm m_PoolType
The pooling algorithm to use (Max. Average, L2).
void ParseEltwiseLayer(const caffe::LayerParameter &layerParam)
static ICaffeParser * CreateRaw()
OutputShapeRounding m_OutputShapeRounding
The rounding method for the output shape. (Floor, Ceiling).
std::vector< const caffe::LayerParameter * > GetInputs(const caffe::LayerParameter &layerParam)
Find the Caffe layers listed as inputs (bottoms) for a given layer.
virtual const IInputSlot & GetInputSlot(unsigned int index) const =0
Get a const input slot handle by slot index.
armnn::TensorInfo GetTensorInfo(unsigned int numberOfBatches, unsigned int numberOfChannels, unsigned int height, unsigned int width, const armnn::DataLayout dataLayout, const armnn::DataType dataType)
virtual const IOutputSlot & GetOutputSlot(unsigned int index) const =0
Get the const output slot handle by slot index.
void ParseLRNLayer(const caffe::LayerParameter &layerParam)
virtual const char * GetName() const =0
Returns the name of the layer.
#define GET_OPTIONAL_WITH_VECTOR_FALLBACK(PARAM, PARAM_TYPE, OPTIONAL_VALUE, FALLBACK_VECTOR, VALUE_TYPE, DEFAULT_VALUE)
void Connect(armnn::IConnectableLayer *from, armnn::IConnectableLayer *to, const armnn::TensorInfo &tensorInfo, unsigned int fromIndex, unsigned int toIndex)
std::unique_ptr< INetwork, void(*)(INetwork *network)> INetworkPtr
virtual int Connect(IInputSlot &destination)=0
A Pooling2dDescriptor for the Pooling2dLayer.
void SetArmnnOutputSlotForCaffeTop(const std::string &caffeTopName, armnn::IOutputSlot &armnnOutputSlot)
A NormalizationDescriptor for the NormalizationLayer.
unsigned int GetNumDimensions() const
void AddConvLayerWithDepthwiseConv(const caffe::LayerParameter &layerParam, const armnn::Convolution2dDescriptor &desc, unsigned int kernelW, unsigned int kernelH)
A SoftmaxDescriptor for the SoftmaxLayer.
float m_Beta
Beta value for the normalization equation.
void ParsePoolingLayer(const caffe::LayerParameter &layerParam)
uint32_t m_NormSize
Depth radius value.
Status SetViewOriginCoord(uint32_t view, uint32_t coord, uint32_t value)
Set the view origin coordinates.
static INetworkPtr Create()
std::map< std::string, const caffe::LayerParameter * > m_CaffeLayersByTopName
ActivationFunction m_Function
The activation function to use (Sigmoid, TanH, Linear, ReLu, BoundedReLu, SoftReLu, LeakyReLu, Abs, Sqrt, Square).
armnn::INetworkPtr m_Network
void ParseConcatLayer(const caffe::LayerParameter &layerParam)
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
A DepthwiseConvolution2dDescriptor for the DepthwiseConvolution2dLayer.
A BatchNormalizationDescriptor for the BatchNormalizationLayer.
uint32_t m_PadLeft
Padding left value in the width dimension.
BlobShape TensorDescToBlobShape(const TensorInfo &desc)
void ParseSplitLayer(const caffe::LayerParameter &layerParam)
Status SetViewOriginCoord(uint32_t view, uint32_t coord, uint32_t value)
Set the view origin coordinates.