blob: 3ab473c2b4a3423dbf263fd9148cb44960456888 [file] [log] [blame]
Laurent Carlier749294b2020-06-01 09:03:17 +01001//
telsoa014fcda012018-03-09 14:13:49 +00002// Copyright © 2017 Arm Ltd. All rights reserved.
David Beckecb56cd2018-09-05 12:52:57 +01003// SPDX-License-Identifier: MIT
telsoa014fcda012018-03-09 14:13:49 +00004//
5#include "CaffeParser.hpp"
telsoa01c577f2c2018-08-31 09:22:23 +01006#include "RecordByRecordCaffeParser.hpp"
telsoa014fcda012018-03-09 14:13:49 +00007
8#include "armnn/Descriptors.hpp"
9#include "armnn/INetwork.hpp"
10#include "armnn/Utils.hpp"
11#include "armnn/Exceptions.hpp"
12
13#include "GraphTopologicalSort.hpp"
telsoa01c577f2c2018-08-31 09:22:23 +010014#include "VerificationHelpers.hpp"
telsoa014fcda012018-03-09 14:13:49 +000015
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +010016#include <armnn/utility/Assert.hpp>
Matthew Sloyan589e3e82020-09-11 16:17:48 +010017#include <armnn/utility/NumericCast.hpp>
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +010018
James Ward58dec6b2020-09-11 17:32:44 +010019#include <fmt/format.h>
telsoa014fcda012018-03-09 14:13:49 +000020
21// Caffe
22#include "caffe/proto/caffe.pb.h"
23
24// ProtoBuf
25#include <google/protobuf/io/coded_stream.h>
26#include <google/protobuf/io/zero_copy_stream.h>
27#include <google/protobuf/io/zero_copy_stream_impl.h>
28#include <google/protobuf/text_format.h>
29#include <google/protobuf/stubs/common.h>
30#include <google/protobuf/stubs/once.h>
31#include <google/protobuf/io/coded_stream.h>
telsoa014fcda012018-03-09 14:13:49 +000032#include <google/protobuf/descriptor.h>
33#include <google/protobuf/generated_message_reflection.h>
34#include <google/protobuf/reflection_ops.h>
35#include <google/protobuf/wire_format.h>
36
37#include <cmath>
38#include <sstream>
39#include <queue>
40#include <fcntl.h>
41
42/// Caffe networks are loaded from protobuf files (binary or text) using the protobuf library and the generated
43/// code from caffe.pb.h. This gives us a caffe::NetParameter which is an in-memory version of the file.
44/// This contains a flat list of Caffe 'layers' (e.g. convolution, pooling etc.).
45/// Each layer has inputs (called "bottoms") and outputs (called "tops"). Data flows from bottom to top.
46/// The bottoms of a layer refer to the tops of other layers, not their names.
telsoa01c577f2c2018-08-31 09:22:23 +010047/// The names of layers seem to be arbitrary (you could rename a layer and the network wouldn't
48/// need any other changes).
telsoa014fcda012018-03-09 14:13:49 +000049///
50/// Some layers (e.g. Relu) can be configured so that their top and bottom are both the same. This is called an
51/// "in-place" layer and is a Caffe runtime feature used to reduce memory usage by modifying tensors in-place.
52/// This isn't relevant to the parser and so we preprocess these layers to convert them to regular layers, to result
53/// in a consistent graph structure.
54
55namespace armnnCaffeParser
56{
57
58using namespace armnn;
59using namespace caffe;
60using namespace std;
61using namespace google::protobuf::io;
62
telsoa01c577f2c2018-08-31 09:22:23 +010063namespace
telsoa014fcda012018-03-09 14:13:49 +000064{
65
telsoa01c577f2c2018-08-31 09:22:23 +010066const float* GetArrayPtrFromBlob(const LayerParameter& layerParam, unsigned int blobIndex)
telsoa014fcda012018-03-09 14:13:49 +000067{
telsoa01c577f2c2018-08-31 09:22:23 +010068 auto nBlobs = layerParam.blobs_size();
Matthew Sloyan589e3e82020-09-11 16:17:48 +010069 if (blobIndex >= armnn::numeric_cast<unsigned int>(nBlobs))
telsoa014fcda012018-03-09 14:13:49 +000070 {
telsoa01c577f2c2018-08-31 09:22:23 +010071 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +010072 fmt::format("Expected data blob at index {} in layer {} not found. nBlobs={}. {}",
73 blobIndex,
74 layerParam.name(),
75 nBlobs,
76 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +000077 }
78
Matthew Sloyan589e3e82020-09-11 16:17:48 +010079 const BlobProto& blob = layerParam.blobs(armnn::numeric_cast<int>(blobIndex));
telsoa014fcda012018-03-09 14:13:49 +000080
telsoa01c577f2c2018-08-31 09:22:23 +010081 const float* arrayPtr = blob.data().data();
82 return arrayPtr;
83}
84
85void GetDataFromBlob(const LayerParameter& layerParam, vector<float>& outData, unsigned int blobIndex)
86{
87 auto nBlobs = layerParam.blobs_size();
Matthew Sloyan589e3e82020-09-11 16:17:48 +010088 if (blobIndex >= armnn::numeric_cast<unsigned int>(nBlobs))
telsoa014fcda012018-03-09 14:13:49 +000089 {
telsoa01c577f2c2018-08-31 09:22:23 +010090 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +010091 fmt::format("Expected data blob at index {} in layer {} not found. {}",
92 blobIndex,
93 layerParam.name(),
94 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +000095 }
96
Matthew Sloyan589e3e82020-09-11 16:17:48 +010097 const BlobProto& blob = layerParam.blobs(armnn::numeric_cast<int>(blobIndex));
telsoa01c577f2c2018-08-31 09:22:23 +010098
Matthew Sloyan589e3e82020-09-11 16:17:48 +010099 size_t blobSize = armnn::numeric_cast<size_t>(blob.data_size());
telsoa01c577f2c2018-08-31 09:22:23 +0100100 if (blobSize != outData.size())
telsoa014fcda012018-03-09 14:13:49 +0000101 {
telsoa01c577f2c2018-08-31 09:22:23 +0100102 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100103 fmt::format("Data blob at index {} in layer {} has an unexpected size. "
104 "Expected {} elements but got {} elements. {}",
105 blobIndex,
106 layerParam.name(),
107 outData.size(),
108 blobSize,
109 CHECK_LOCATION().AsString()));
telsoa01c577f2c2018-08-31 09:22:23 +0100110 }
111
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100112 int outSizeInt = armnn::numeric_cast<int>(outData.size());
telsoa01c577f2c2018-08-31 09:22:23 +0100113 for (int i = 0; i < outSizeInt; ++i)
114 {
115 outData[static_cast<size_t>(i)] = blob.data(i);
telsoa014fcda012018-03-09 14:13:49 +0000116 }
117}
118
telsoa014fcda012018-03-09 14:13:49 +0000119template <typename T>
120size_t SizeOfVectorData(const vector<T>& vec)
121{
122 return vec.size() * sizeof(T);
123}
124
125void ValidateNumInputsOutputs(const caffe::LayerParameter& layerParameter,
126 unsigned int numInputs,
127 unsigned int numOutputs)
128{
129 int numInputsActual = layerParameter.bottom_size();
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100130 if (numInputs != armnn::numeric_cast<unsigned int>(numInputsActual))
telsoa014fcda012018-03-09 14:13:49 +0000131 {
telsoa01c577f2c2018-08-31 09:22:23 +0100132 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100133 fmt::format("Invalid number of inputs requested {} for layer {} "
134 "while only {} present. {}",
135 numInputs,
136 layerParameter.name(),
137 numInputsActual,
138 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +0000139 }
140
141 int numOutputsActual = layerParameter.top_size();
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100142 if (numOutputs != armnn::numeric_cast<unsigned int>(numOutputsActual))
telsoa014fcda012018-03-09 14:13:49 +0000143 {
telsoa01c577f2c2018-08-31 09:22:23 +0100144 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100145 fmt::format("Invalid number of outputs requested {} for layer {} "
146 "while only {} present. {}",
147 numOutputs,
148 layerParameter.name(),
149 numOutputsActual,
150 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +0000151 }
152}
153
telsoa01c577f2c2018-08-31 09:22:23 +0100154template <typename ParamType, typename ExtractOptional, typename ExtractFallback, typename ValueType>
155ValueType GetOptionalWithFallback(const ParamType& param,
156 ExtractOptional extractOptional,
157 ExtractFallback extractFallback,
158 ValueType defaultValue)
159{
160 auto optValue = extractOptional(param, defaultValue);
161 if (optValue.first)
162 {
163 return optValue.second;
164 }
165 auto fallbackValue = extractFallback(param, defaultValue);
166 return fallbackValue.second;
167}
168
169#define GET_OPTIONAL_WITH_VECTOR_FALLBACK(PARAM, \
170 PARAM_TYPE, \
171 OPTIONAL_VALUE, \
172 FALLBACK_VECTOR, \
173 VALUE_TYPE, \
174 DEFAULT_VALUE) \
175 GetOptionalWithFallback( \
176 PARAM, \
177 [](const PARAM_TYPE & param, VALUE_TYPE defaultValue) \
178 { \
179 if (param.has_##OPTIONAL_VALUE ()) \
180 { \
181 return std::make_pair(true, param.OPTIONAL_VALUE ()); \
182 } \
183 else \
184 { \
185 return std::make_pair(false, defaultValue); \
186 } \
187 }, \
188 [](const PARAM_TYPE & param, VALUE_TYPE defaultValue) \
189 { \
190 if (param.FALLBACK_VECTOR##_size() > 0) \
191 { \
192 return std::make_pair(true, (param.FALLBACK_VECTOR ()).Get(0)); \
193 } \
194 else \
195 { \
196 return std::make_pair(false, defaultValue); \
197 } \
198 }, \
199 DEFAULT_VALUE)
200
201#define GET_OPTIONAL_WITH_FALLBACK(PARAM, \
202 PARAM_TYPE, \
203 OPTIONAL_VALUE, \
204 FALLBACK_VALUE, \
205 VALUE_TYPE, \
206 DEFAULT_VALUE) \
207 GetOptionalWithFallback( \
208 PARAM, \
209 [](const PARAM_TYPE & param, VALUE_TYPE defaultValue) \
210 { \
211 if (param.has_##OPTIONAL_VALUE ()) \
212 { \
213 return std::make_pair(true, param.OPTIONAL_VALUE ()); \
214 } \
215 else \
216 { \
217 return std::make_pair(false, defaultValue); \
218 } \
219 }, \
220 [](const PARAM_TYPE & param, VALUE_TYPE defaultValue) \
221 { \
222 if (param.has_##FALLBACK_VALUE ()) \
223 { \
224 return std::make_pair(true, param.FALLBACK_VALUE ()); \
225 } \
226 else \
227 { \
228 return std::make_pair(false, defaultValue); \
229 } \
230 }, \
231 DEFAULT_VALUE)
232
telsoa01c577f2c2018-08-31 09:22:23 +0100233} // namespace <anonymous>
234
235const std::map<std::string, CaffeParserBase::OperationParsingFunction>
236 CaffeParserBase::ms_CaffeLayerNameToParsingFunctions = {
237 { "Input", &CaffeParserBase::ParseInputLayer },
238 { "Convolution", &CaffeParserBase::ParseConvLayer },
Keith Mok9801b172020-12-20 19:47:25 -0800239 { "Deconvolution",&CaffeParserBase::ParseDeconvLayer },
telsoa01c577f2c2018-08-31 09:22:23 +0100240 { "Pooling", &CaffeParserBase::ParsePoolingLayer },
241 { "ReLU", &CaffeParserBase::ParseReluLayer },
242 { "LRN", &CaffeParserBase::ParseLRNLayer },
243 { "InnerProduct", &CaffeParserBase::ParseInnerProductLayer },
244 { "Softmax", &CaffeParserBase::ParseSoftmaxLayer },
245 { "Eltwise", &CaffeParserBase::ParseEltwiseLayer },
246 { "Concat", &CaffeParserBase::ParseConcatLayer },
247 { "BatchNorm", &CaffeParserBase::ParseBatchNormLayer },
248 { "Scale", &CaffeParserBase::ParseScaleLayer },
249 { "Split", &CaffeParserBase::ParseSplitLayer },
250 { "Dropout", &CaffeParserBase::ParseDropoutLayer},
Keith Mok9801b172020-12-20 19:47:25 -0800251 { "ArgMax", &CaffeParserBase::ParseArgmaxLayer},
telsoa01c577f2c2018-08-31 09:22:23 +0100252};
253
254ICaffeParser* ICaffeParser::CreateRaw()
255{
256 return new RecordByRecordCaffeParser();
257}
258
259ICaffeParserPtr ICaffeParser::Create()
260{
261 return ICaffeParserPtr(CreateRaw(), &ICaffeParser::Destroy);
262}
263
264void ICaffeParser::Destroy(ICaffeParser* parser)
265{
266 delete parser;
267}
268
269CaffeParserBase::CaffeParserBase()
270 : m_Network(nullptr, nullptr)
271{
272
273}
274
275CaffeParser::CaffeParser()
276: CaffeParserBase()
277{
278
279}
280
281BindingPointInfo CaffeParserBase::GetNetworkInputBindingInfo(const std::string& name) const
telsoa014fcda012018-03-09 14:13:49 +0000282{
283 return GetBindingInfo(name, "input", m_NetworkInputsBindingInfo);
284}
285
telsoa01c577f2c2018-08-31 09:22:23 +0100286BindingPointInfo CaffeParserBase::GetNetworkOutputBindingInfo(const std::string& name) const
telsoa014fcda012018-03-09 14:13:49 +0000287{
288 return GetBindingInfo(name, "output", m_NetworkOutputsBindingInfo);
289}
290
telsoa01c577f2c2018-08-31 09:22:23 +0100291std::pair<armnn::LayerBindingId, armnn::TensorInfo> CaffeParserBase::GetBindingInfo(const std::string& layerName,
telsoa014fcda012018-03-09 14:13:49 +0000292 const char* bindingPointDesc,
293 const std::unordered_map<std::string, BindingPointInfo>& nameToBindingInfo)
294{
295 auto it = nameToBindingInfo.find(layerName);
296 if (it == nameToBindingInfo.end())
297 {
telsoa01c577f2c2018-08-31 09:22:23 +0100298 throw InvalidArgumentException(
James Ward58dec6b2020-09-11 17:32:44 +0100299 fmt::format("Unknown binding {} for layer '{}'. {}",
300 bindingPointDesc,
301 layerName,
302 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +0000303 }
304 return it->second;
305}
306
telsoa01c577f2c2018-08-31 09:22:23 +0100307TensorInfo CaffeParserBase::BlobShapeToTensorInfo(const caffe::BlobShape& blobShape) const
telsoa014fcda012018-03-09 14:13:49 +0000308{
309 std::vector<unsigned int> shape;
310 for (int j = 0; j < blobShape.dim_size(); ++j)
311 {
312 shape.push_back(static_cast<unsigned int>(blobShape.dim(j)));
313 }
314
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100315 return TensorInfo(armnn::numeric_cast<unsigned int>(shape.size()), shape.data(), DataType::Float32);
telsoa014fcda012018-03-09 14:13:49 +0000316}
317
318BlobShape TensorDescToBlobShape(const TensorInfo& desc)
319{
320 BlobShape ret;
321 for (unsigned int i = 0; i < desc.GetNumDimensions(); ++i)
322 {
323 ret.add_dim(i);
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100324 ret.set_dim(armnn::numeric_cast<int>(i), desc.GetShape()[i]);
telsoa014fcda012018-03-09 14:13:49 +0000325 }
326
327 return ret;
328}
329
telsoa01c577f2c2018-08-31 09:22:23 +0100330// Note: can move to CaffeParser when/if we optimise the text/string format
331// to load on a layer by layer basis
332vector<const LayerParameter*> CaffeParserBase::GetInputs(const LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +0000333{
334 std::vector<const caffe::LayerParameter*> ret;
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100335 ret.reserve(armnn::numeric_cast<size_t>(layerParam.bottom_size()));
telsoa014fcda012018-03-09 14:13:49 +0000336 for (int j = 0; j < layerParam.bottom_size(); ++j)
337 {
338 std::string inputName = layerParam.bottom(j);
339 auto inputIt = m_CaffeLayersByTopName.find(inputName);
340 if (inputIt == m_CaffeLayersByTopName.end())
341 {
342 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100343 fmt::format("Can't find Caffe layer with top called '{}', "
344 "which is listed as an input of '{}'. {}",
345 inputName,
346 layerParam.name(),
347 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +0000348 }
349 ret.push_back(inputIt->second);
350 }
351
352 return ret;
353}
354
telsoa01c577f2c2018-08-31 09:22:23 +0100355void CaffeParserBase::ParseInputLayer(const LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +0000356{
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100357 ARMNN_ASSERT(layerParam.type() == "Input");
telsoa014fcda012018-03-09 14:13:49 +0000358 ValidateNumInputsOutputs(layerParam, 0, 1);
359
360 const InputParameter& param = layerParam.input_param();
361
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100362 const armnn::LayerBindingId inputId = armnn::numeric_cast<armnn::LayerBindingId>(
telsoa01c577f2c2018-08-31 09:22:23 +0100363 m_NetworkInputsBindingInfo.size());
telsoa014fcda012018-03-09 14:13:49 +0000364 armnn::IConnectableLayer* const inputLayer = m_Network->AddInputLayer(inputId, layerParam.name().c_str());
365
telsoa01c577f2c2018-08-31 09:22:23 +0100366 // Decides the tensor info for this input. This can be specified in the Caffe network but can also
telsoa014fcda012018-03-09 14:13:49 +0000367 // be overriden by user input (m_inputShapes).
368 armnn::TensorInfo inputTensorInfo;
369
370 const BlobShape* originalShape = param.shape_size() > 0 && param.shape(0).dim_size() > 0 ?
371 &param.shape(0) : nullptr;
372 if (originalShape)
373 {
374 inputTensorInfo = BlobShapeToTensorInfo(*originalShape);
375 }
376
377 auto overrideIt = m_InputShapes.find(layerParam.name());
378 if (overrideIt != m_InputShapes.end())
379 {
380 const TensorShape& overrideShape = overrideIt->second;
381 if (originalShape &&
382 ( originalShape->dim(1) != overrideShape[1]
383 || originalShape->dim(2) != overrideShape[2]
384 || originalShape->dim(3) != overrideShape[3]))
385 {
telsoa01c577f2c2018-08-31 09:22:23 +0100386 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100387 fmt::format("Parsed input shape for '{}' is incompatible with the override provided. {}",
388 layerParam.name(),
389 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +0000390 }
391 inputTensorInfo.SetShape(overrideShape);
392 }
393 else if (!originalShape)
394 {
telsoa01c577f2c2018-08-31 09:22:23 +0100395 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100396 fmt::format("No input descriptor given for '{}' and no input shape found in caffe model. {}",
397 layerParam.name(),
398 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +0000399 }
telsoa014fcda012018-03-09 14:13:49 +0000400 TrackInputBinding(inputLayer, inputId, inputTensorInfo);
401 inputLayer->GetOutputSlot(0).SetTensorInfo(inputTensorInfo);
402 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), inputLayer->GetOutputSlot(0));
403}
404
telsoa01c577f2c2018-08-31 09:22:23 +0100405void CaffeParserBase::AddConvLayerWithSplits(const caffe::LayerParameter& layerParam,
406 const armnn::Convolution2dDescriptor& desc,
407 unsigned int kernelW,
408 unsigned int kernelH)
telsoa014fcda012018-03-09 14:13:49 +0000409{
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100410 ARMNN_ASSERT(layerParam.type() == "Convolution");
telsoa014fcda012018-03-09 14:13:49 +0000411 ValidateNumInputsOutputs(layerParam, 1, 1);
412
telsoa01c577f2c2018-08-31 09:22:23 +0100413 ConvolutionParameter convParam = layerParam.convolution_param();
telsoa014fcda012018-03-09 14:13:49 +0000414 BlobShape inputShape = TensorDescToBlobShape(GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo());
telsoa01c577f2c2018-08-31 09:22:23 +0100415 const unsigned int numGroups = convParam.has_group() ? convParam.group() : 1;
telsoa014fcda012018-03-09 14:13:49 +0000416
telsoa01c577f2c2018-08-31 09:22:23 +0100417 // asusme these were already verified by the caller ParseConvLayer() function
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100418 ARMNN_ASSERT(numGroups < inputShape.dim(1));
419 ARMNN_ASSERT(numGroups > 1);
telsoa014fcda012018-03-09 14:13:49 +0000420
421 // Handle grouping
telsoa014fcda012018-03-09 14:13:49 +0000422 armnn::IOutputSlot& inputConnection = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0));
423
424 vector<string> convLayerNames(numGroups);
425 vector<armnn::IConnectableLayer*> convLayers(numGroups);
426 convLayerNames[0] = layerParam.name();
427
telsoa01c577f2c2018-08-31 09:22:23 +0100428 // This convolution is to be applied to chunks of the input data so add a splitter layer
429
430 // Redirect the convolution input to the splitter
431 unsigned int splitterDimSizes[4] = {static_cast<unsigned int>(inputShape.dim(0)),
432 static_cast<unsigned int>(inputShape.dim(1)),
433 static_cast<unsigned int>(inputShape.dim(2)),
434 static_cast<unsigned int>(inputShape.dim(3))};
435
436 // Split dimension 1 of the splitter output shape and conv input shapes
437 // according to the number of groups
438
439 splitterDimSizes[1] /= numGroups;
440 inputShape.set_dim(1, splitterDimSizes[1]);
441
442 // This is used to describe how the input is to be split
443 ViewsDescriptor splitterDesc(numGroups);
444
445 // Create an output node for each group, giving each a unique name
446 for (unsigned int g = 0; g < numGroups; ++g)
telsoa014fcda012018-03-09 14:13:49 +0000447 {
telsoa01c577f2c2018-08-31 09:22:23 +0100448 // Work out the names of the splitter layers child convolutions
449 stringstream ss;
450 ss << layerParam.name() << "_" << g;
451 convLayerNames[g] = ss.str();
telsoa014fcda012018-03-09 14:13:49 +0000452
telsoa01c577f2c2018-08-31 09:22:23 +0100453 splitterDesc.SetViewOriginCoord(g, 1, splitterDimSizes[1] * g);
telsoa014fcda012018-03-09 14:13:49 +0000454
telsoa01c577f2c2018-08-31 09:22:23 +0100455 // Set the size of the views.
456 for (unsigned int dimIdx=0; dimIdx < 4; dimIdx++)
telsoa014fcda012018-03-09 14:13:49 +0000457 {
telsoa01c577f2c2018-08-31 09:22:23 +0100458 splitterDesc.SetViewSize(g, dimIdx, splitterDimSizes[dimIdx]);
telsoa014fcda012018-03-09 14:13:49 +0000459 }
460 }
461
telsoa01c577f2c2018-08-31 09:22:23 +0100462 const std::string splitterLayerName = std::string("splitter_") + layerParam.bottom(0);
463 armnn::IConnectableLayer* splitterLayer = m_Network->AddSplitterLayer(splitterDesc, splitterLayerName.c_str());
telsoa014fcda012018-03-09 14:13:49 +0000464
telsoa01c577f2c2018-08-31 09:22:23 +0100465 inputConnection.Connect(splitterLayer->GetInputSlot(0));
466 for (unsigned int i = 0; i < splitterLayer->GetNumOutputSlots(); i++)
467 {
468 splitterLayer->GetOutputSlot(i).SetTensorInfo(BlobShapeToTensorInfo(inputShape));
469 }
telsoa014fcda012018-03-09 14:13:49 +0000470
471 unsigned int numFilters = convParam.num_output();
472
telsoa01c577f2c2018-08-31 09:22:23 +0100473 // Populates convolution output tensor descriptor dimensions.
telsoa014fcda012018-03-09 14:13:49 +0000474 BlobShape outputShape;
475 outputShape.add_dim(0);
476 outputShape.set_dim(0, inputShape.dim(0));
477 outputShape.add_dim(1);
telsoa01c577f2c2018-08-31 09:22:23 +0100478 // Ensures that dimension 1 of the convolution output is split according to the number of groups.
telsoa014fcda012018-03-09 14:13:49 +0000479 outputShape.set_dim(1, numFilters / numGroups);
480 outputShape.add_dim(2);
481 outputShape.set_dim(
telsoa01c577f2c2018-08-31 09:22:23 +0100482 2, (static_cast<int>(
Keith Mok7dc18202020-12-20 13:45:51 -0800483 static_cast<float>(inputShape.dim(2) + 2 * desc.m_PadBottom - (desc.m_DilationX * (kernelH - 1) + 1)) /
telsoa01c577f2c2018-08-31 09:22:23 +0100484 static_cast<float>(desc.m_StrideY)) + 1));
telsoa014fcda012018-03-09 14:13:49 +0000485 outputShape.add_dim(3);
486 outputShape.set_dim(
telsoa01c577f2c2018-08-31 09:22:23 +0100487 3, (static_cast<int>(
Keith Mok7dc18202020-12-20 13:45:51 -0800488 static_cast<float>(inputShape.dim(3) + 2 * desc.m_PadRight - (desc.m_DilationY * (kernelW - 1) + 1)) /
telsoa01c577f2c2018-08-31 09:22:23 +0100489 static_cast<float>(desc.m_StrideX)) + 1));
telsoa014fcda012018-03-09 14:13:49 +0000490
491 // Load the weight data for ALL groups
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100492 vector<float> weightData(armnn::numeric_cast<size_t>(numGroups *
telsoa01c577f2c2018-08-31 09:22:23 +0100493 inputShape.dim(1) * // number of input channels
494 outputShape.dim(1) * // number of output channels
495 kernelH *
496 kernelW));
telsoa014fcda012018-03-09 14:13:49 +0000497 GetDataFromBlob(layerParam, weightData, 0);
498
499 const unsigned int weightDimSizes[4] = {
telsoa01c577f2c2018-08-31 09:22:23 +0100500 static_cast<unsigned int>(outputShape.dim(1)),
501 static_cast<unsigned int>(inputShape.dim(1)),
502 kernelH,
503 kernelW};
telsoa014fcda012018-03-09 14:13:49 +0000504
telsoa014fcda012018-03-09 14:13:49 +0000505 TensorInfo biasInfo;
506 vector<float> biasData;
telsoa01c577f2c2018-08-31 09:22:23 +0100507
508 if (desc.m_BiasEnabled)
telsoa014fcda012018-03-09 14:13:49 +0000509 {
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100510 biasData.resize(armnn::numeric_cast<size_t>(numGroups * outputShape.dim(1)), 1.f);
telsoa014fcda012018-03-09 14:13:49 +0000511 GetDataFromBlob(layerParam, biasData, 1);
512
513 const unsigned int biasDimSizes[1] = {static_cast<unsigned int>(outputShape.dim(1))};
514 biasInfo = TensorInfo(1, biasDimSizes, DataType::Float32);
515 }
516
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100517 const unsigned int numWeightsPerGroup = armnn::numeric_cast<unsigned int>(weightData.size()) / numGroups;
518 const unsigned int numBiasesPerGroup = armnn::numeric_cast<unsigned int>(biasData.size()) / numGroups;
telsoa014fcda012018-03-09 14:13:49 +0000519
telsoa014fcda012018-03-09 14:13:49 +0000520 for (unsigned int g = 0; g < numGroups; ++g)
521 {
telsoa01c577f2c2018-08-31 09:22:23 +0100522 // Sets the slot index, group 0 should be connected to the 0th output of the splitter
523 // group 1 should be connected to the 1st output of the splitter.
telsoa014fcda012018-03-09 14:13:49 +0000524
telsoa01c577f2c2018-08-31 09:22:23 +0100525 // Pulls out the weights for this group from that loaded from the model file earlier.
telsoa014fcda012018-03-09 14:13:49 +0000526 ConstTensor weights(TensorInfo(4, weightDimSizes, DataType::Float32),
527 weightData.data() + numWeightsPerGroup * g);
528
529 IConnectableLayer* convLayer = nullptr;
Matteo Martincighfc598e12019-05-14 10:36:13 +0100530 Optional<ConstTensor> optionalBiases;
telsoa01c577f2c2018-08-31 09:22:23 +0100531 if (desc.m_BiasEnabled)
telsoa014fcda012018-03-09 14:13:49 +0000532 {
telsoa01c577f2c2018-08-31 09:22:23 +0100533 // Pulls out the biases for this group from that loaded from the model file earlier.
telsoa014fcda012018-03-09 14:13:49 +0000534 ConstTensor biases(biasInfo, biasData.data() + numBiasesPerGroup * g);
Matteo Martincighfc598e12019-05-14 10:36:13 +0100535 optionalBiases = Optional<ConstTensor>(biases);
telsoa014fcda012018-03-09 14:13:49 +0000536 }
Matteo Martincighfc598e12019-05-14 10:36:13 +0100537 convLayer = m_Network->AddConvolution2dLayer(desc,
538 weights,
539 optionalBiases,
540 convLayerNames[g].c_str());
telsoa014fcda012018-03-09 14:13:49 +0000541 convLayers[g] = convLayer;
542
543 // If we have more than one group then the input to the nth convolution the splitter layer's nth output,
544 // otherwise it's the regular input to this layer.
telsoa01c577f2c2018-08-31 09:22:23 +0100545 armnn::IOutputSlot& splitterInputConnection =
546 splitterLayer ? splitterLayer->GetOutputSlot(g) : inputConnection;
telsoa014fcda012018-03-09 14:13:49 +0000547 splitterInputConnection.Connect(convLayer->GetInputSlot(0));
548 convLayer->GetOutputSlot(0).SetTensorInfo(BlobShapeToTensorInfo(outputShape));
telsoa014fcda012018-03-09 14:13:49 +0000549 }
550
Jim Flynne242f2d2019-05-22 14:24:13 +0100551 // If the convolution was performed in chunks, add a layer to concatenate the results
telsoa01c577f2c2018-08-31 09:22:23 +0100552
553 // The merge input shape matches that of the convolution output
Jim Flynne242f2d2019-05-22 14:24:13 +0100554 unsigned int concatDimSizes[4] = {static_cast<unsigned int>(outputShape.dim(0)),
555 static_cast<unsigned int>(outputShape.dim(1)),
556 static_cast<unsigned int>(outputShape.dim(2)),
557 static_cast<unsigned int>(outputShape.dim(3))};
telsoa01c577f2c2018-08-31 09:22:23 +0100558
Jim Flynne242f2d2019-05-22 14:24:13 +0100559 // This is used to describe how the input is to be concatenated
560 OriginsDescriptor concatDesc(numGroups);
telsoa01c577f2c2018-08-31 09:22:23 +0100561
562 // Now create an input node for each group, using the name from
563 // the output of the corresponding convolution
564 for (unsigned int g = 0; g < numGroups; ++g)
telsoa014fcda012018-03-09 14:13:49 +0000565 {
Jim Flynne242f2d2019-05-22 14:24:13 +0100566 concatDesc.SetViewOriginCoord(g, 1, concatDimSizes[1] * g);
telsoa01c577f2c2018-08-31 09:22:23 +0100567 }
telsoa014fcda012018-03-09 14:13:49 +0000568
Jim Flynne242f2d2019-05-22 14:24:13 +0100569 // Make sure the output from the concat is the correct size to hold the data for all groups
570 concatDimSizes[1] *= numGroups;
571 outputShape.set_dim(1, concatDimSizes[1]);
telsoa014fcda012018-03-09 14:13:49 +0000572
Jim Flynne242f2d2019-05-22 14:24:13 +0100573 // Finally add the concat layer
574 IConnectableLayer* concatLayer = m_Network->AddConcatLayer(concatDesc, layerParam.name().c_str());
telsoa014fcda012018-03-09 14:13:49 +0000575
Jim Flynne242f2d2019-05-22 14:24:13 +0100576 if (!concatLayer)
telsoa01c577f2c2018-08-31 09:22:23 +0100577 {
578 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100579 fmt::format("Failed to create final concat layer for Split+Convolution+Concat. "
580 "Layer={} #groups={} #filters={} {}",
581 layerParam.name(),
582 numGroups,
583 numFilters,
584 CHECK_LOCATION().AsString()));
telsoa01c577f2c2018-08-31 09:22:23 +0100585 }
telsoa014fcda012018-03-09 14:13:49 +0000586
telsoa01c577f2c2018-08-31 09:22:23 +0100587 for (unsigned int g = 0; g < numGroups; ++g)
588 {
Jim Flynne242f2d2019-05-22 14:24:13 +0100589 convLayers[g]->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(g));
telsoa01c577f2c2018-08-31 09:22:23 +0100590 }
Jim Flynne242f2d2019-05-22 14:24:13 +0100591 concatLayer->GetOutputSlot(0).SetTensorInfo(armnn::TensorInfo(4, concatDimSizes, DataType::Float32));
592 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), concatLayer->GetOutputSlot(0));
telsoa01c577f2c2018-08-31 09:22:23 +0100593}
telsoa014fcda012018-03-09 14:13:49 +0000594
Keith Mok9801b172020-12-20 19:47:25 -0800595void CaffeParserBase::AddDeconvLayerWithSplits(const caffe::LayerParameter& layerParam,
596 const armnn::TransposeConvolution2dDescriptor& desc,
597 unsigned int kernelW,
598 unsigned int kernelH)
599{
600 ARMNN_ASSERT(layerParam.type() == "Deconvolution");
601 ValidateNumInputsOutputs(layerParam, 1, 1);
602
603 ConvolutionParameter convParam = layerParam.convolution_param();
604 BlobShape inputShape = TensorDescToBlobShape(GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo());
605 const unsigned int numGroups = convParam.has_group() ? convParam.group() : 1;
606
607 // asusme these were already verified by the caller ParseDeconvLayer() function
608 ARMNN_ASSERT(numGroups <= inputShape.dim(1));
609 ARMNN_ASSERT(numGroups > 1);
610
611 // Handle grouping
612 armnn::IOutputSlot& inputConnection = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0));
613
614 vector<string> convLayerNames(numGroups);
615 vector<armnn::IConnectableLayer*> convLayers(numGroups);
616 convLayerNames[0] = layerParam.name();
617
618 // This deconvolution is to be applied to chunks of the input data so add a splitter layer
619
620 // Redirect the deconvolution input to the splitter
621 unsigned int splitterDimSizes[4] = {static_cast<unsigned int>(inputShape.dim(0)),
622 static_cast<unsigned int>(inputShape.dim(1)),
623 static_cast<unsigned int>(inputShape.dim(2)),
624 static_cast<unsigned int>(inputShape.dim(3))};
625
626 // Split dimension 1 of the splitter output shape and deconv input shapes
627 // according to the number of groups
628
629 splitterDimSizes[1] /= numGroups;
630 inputShape.set_dim(1, splitterDimSizes[1]);
631
632 // This is used to describe how the input is to be split
633 ViewsDescriptor splitterDesc(numGroups);
634
635 // Create an output node for each group, giving each a unique name
636 for (unsigned int g = 0; g < numGroups; ++g)
637 {
638 // Work out the names of the splitter layers child deconvolutions
639 stringstream ss;
640 ss << layerParam.name() << "_" << g;
641 convLayerNames[g] = ss.str();
642
643 splitterDesc.SetViewOriginCoord(g, 1, splitterDimSizes[1] * g);
644
645 // Set the size of the views.
646 for (unsigned int dimIdx=0; dimIdx < 4; dimIdx++)
647 {
648 splitterDesc.SetViewSize(g, dimIdx, splitterDimSizes[dimIdx]);
649 }
650 }
651
652 const std::string splitterLayerName = std::string("splitter_") + layerParam.bottom(0);
653 armnn::IConnectableLayer* splitterLayer = m_Network->AddSplitterLayer(splitterDesc, splitterLayerName.c_str());
654
655 inputConnection.Connect(splitterLayer->GetInputSlot(0));
656 for (unsigned int i = 0; i < splitterLayer->GetNumOutputSlots(); i++)
657 {
658 splitterLayer->GetOutputSlot(i).SetTensorInfo(BlobShapeToTensorInfo(inputShape));
659 }
660
661 unsigned int numFilters = convParam.num_output();
662
663 // Populates deconvolution output tensor descriptor dimensions.
664 BlobShape outputShape;
665 outputShape.add_dim(0);
666 outputShape.set_dim(0, inputShape.dim(0));
667 outputShape.add_dim(1);
668 // Ensures that dimension 1 of the deconvolution output is split according to the number of groups.
669 outputShape.set_dim(1, numFilters / numGroups);
670 outputShape.add_dim(2);
671 outputShape.set_dim(
672 2, (static_cast<int>(
673 desc.m_StrideY * (inputShape.dim(2) - 1) - 2 * desc.m_PadBottom + kernelH)));
674 outputShape.add_dim(3);
675 outputShape.set_dim(
676 3, (static_cast<int>(
677 desc.m_StrideX * (inputShape.dim(3) - 1) - 2 * desc.m_PadRight + kernelW)));
678
679 // Load the weight data for ALL groups
680 vector<float> weightData(armnn::numeric_cast<size_t>(numGroups *
681 inputShape.dim(1) * // number of input channels
682 outputShape.dim(1) * // number of output channels
683 kernelH *
684 kernelW));
685 GetDataFromBlob(layerParam, weightData, 0);
686
687 const unsigned int weightDimSizes[4] = {
688 static_cast<unsigned int>(outputShape.dim(1)),
689 static_cast<unsigned int>(inputShape.dim(1)),
690 kernelH,
691 kernelW};
692
693 TensorInfo biasInfo;
694 vector<float> biasData;
695
696 if (desc.m_BiasEnabled)
697 {
698 biasData.resize(armnn::numeric_cast<size_t>(numGroups * outputShape.dim(1)), 1.f);
699 GetDataFromBlob(layerParam, biasData, 1);
700
701 const unsigned int biasDimSizes[1] = {static_cast<unsigned int>(outputShape.dim(1))};
702 biasInfo = TensorInfo(1, biasDimSizes, DataType::Float32);
703 }
704
705 const unsigned int numWeightsPerGroup = armnn::numeric_cast<unsigned int>(weightData.size()) / numGroups;
706 const unsigned int numBiasesPerGroup = armnn::numeric_cast<unsigned int>(biasData.size()) / numGroups;
707
708 for (unsigned int g = 0; g < numGroups; ++g)
709 {
710 // Sets the slot index, group 0 should be connected to the 0th output of the splitter
711 // group 1 should be connected to the 1st output of the splitter.
712
713 // Pulls out the weights for this group from that loaded from the model file earlier.
714 ConstTensor weights(TensorInfo(4, weightDimSizes, DataType::Float32),
715 weightData.data() + numWeightsPerGroup * g);
716
717 IConnectableLayer* deconvLayer = nullptr;
718 Optional<ConstTensor> optionalBiases;
719 if (desc.m_BiasEnabled)
720 {
721 // Pulls out the biases for this group from that loaded from the model file earlier.
722 ConstTensor biases(biasInfo, biasData.data() + numBiasesPerGroup * g);
723 optionalBiases = Optional<ConstTensor>(biases);
724 }
725 deconvLayer = m_Network->AddTransposeConvolution2dLayer(desc,
726 weights,
727 optionalBiases,
728 convLayerNames[g].c_str());
729 convLayers[g] = deconvLayer;
730
731 // If we have more than one group then the input to the nth deconvolution the splitter layer's nth output,
732 // otherwise it's the regular input to this layer.
733 armnn::IOutputSlot& splitterInputConnection =
734 splitterLayer ? splitterLayer->GetOutputSlot(g) : inputConnection;
735 splitterInputConnection.Connect(deconvLayer->GetInputSlot(0));
736 deconvLayer->GetOutputSlot(0).SetTensorInfo(BlobShapeToTensorInfo(outputShape));
737 }
738
739 // If the deconvolution was performed in chunks, add a layer to concatenate the results
740
741 // The merge input shape matches that of the deconvolution output
742 unsigned int concatDimSizes[4] = {static_cast<unsigned int>(outputShape.dim(0)),
743 static_cast<unsigned int>(outputShape.dim(1)),
744 static_cast<unsigned int>(outputShape.dim(2)),
745 static_cast<unsigned int>(outputShape.dim(3))};
746
747 // This is used to describe how the input is to be concatenated
748 OriginsDescriptor concatDesc(numGroups);
749
750 // Now create an input node for each group, using the name from
751 // the output of the corresponding deconvolution
752 for (unsigned int g = 0; g < numGroups; ++g)
753 {
754 concatDesc.SetViewOriginCoord(g, 1, concatDimSizes[1] * g);
755 }
756
757 // Make sure the output from the concat is the correct size to hold the data for all groups
758 concatDimSizes[1] *= numGroups;
759 outputShape.set_dim(1, concatDimSizes[1]);
760
761 // Finally add the concat layer
762 IConnectableLayer* concatLayer = m_Network->AddConcatLayer(concatDesc, layerParam.name().c_str());
763
764 if (!concatLayer)
765 {
766 throw ParseException(
767 fmt::format("Failed to create final concat layer for Split+Deconvolution+Concat. "
768 "Layer={} #groups={} #filters={} {}",
769 layerParam.name(),
770 numGroups,
771 numFilters,
772 CHECK_LOCATION().AsString()));
773 }
774
775 for (unsigned int g = 0; g < numGroups; ++g)
776 {
777 convLayers[g]->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(g));
778 }
779 concatLayer->GetOutputSlot(0).SetTensorInfo(armnn::TensorInfo(4, concatDimSizes, DataType::Float32));
780 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), concatLayer->GetOutputSlot(0));
781}
782
telsoa01c577f2c2018-08-31 09:22:23 +0100783void CaffeParserBase::AddConvLayerWithDepthwiseConv(const caffe::LayerParameter& layerParam,
784 const armnn::Convolution2dDescriptor& convDesc,
785 unsigned int kernelW,
786 unsigned int kernelH)
787{
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100788 ARMNN_ASSERT(layerParam.type() == "Convolution");
telsoa01c577f2c2018-08-31 09:22:23 +0100789 ValidateNumInputsOutputs(layerParam, 1, 1);
telsoa014fcda012018-03-09 14:13:49 +0000790
telsoa01c577f2c2018-08-31 09:22:23 +0100791 ConvolutionParameter convParam = layerParam.convolution_param();
792 BlobShape inputShape = TensorDescToBlobShape(GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo());
telsoa014fcda012018-03-09 14:13:49 +0000793
telsoa01c577f2c2018-08-31 09:22:23 +0100794 DepthwiseConvolution2dDescriptor desc;
795 desc.m_PadLeft = convDesc.m_PadLeft;
796 desc.m_PadRight = convDesc.m_PadRight;
797 desc.m_PadTop = convDesc.m_PadTop;
798 desc.m_PadBottom = convDesc.m_PadBottom;
799 desc.m_StrideX = convDesc.m_StrideX;
800 desc.m_StrideY = convDesc.m_StrideY;
Keith Mok7dc18202020-12-20 13:45:51 -0800801 desc.m_DilationX = convDesc.m_DilationX;
802 desc.m_DilationY = convDesc.m_DilationY;
telsoa01c577f2c2018-08-31 09:22:23 +0100803 desc.m_BiasEnabled = convDesc.m_BiasEnabled;
telsoa014fcda012018-03-09 14:13:49 +0000804
telsoa01c577f2c2018-08-31 09:22:23 +0100805 unsigned int numFilters = convParam.num_output();
806
807 BlobShape outputShape;
808 outputShape.add_dim(0);
809 outputShape.set_dim(0, inputShape.dim(0));
810 outputShape.add_dim(1);
811 outputShape.set_dim(1, numFilters);
812 outputShape.add_dim(2);
813 outputShape.set_dim(
814 2, (static_cast<int>(
Keith Mok7dc18202020-12-20 13:45:51 -0800815 static_cast<float>(inputShape.dim(2) + 2 * desc.m_PadBottom - (desc.m_DilationX * (kernelH - 1) + 1)) /
telsoa01c577f2c2018-08-31 09:22:23 +0100816 static_cast<float>(desc.m_StrideY)) + 1));
817 outputShape.add_dim(3);
818 outputShape.set_dim(
819 3, (static_cast<int>(
Keith Mok7dc18202020-12-20 13:45:51 -0800820 static_cast<float>(inputShape.dim(3) + 2 * desc.m_PadRight - (desc.m_DilationY * (kernelW - 1) + 1)) /
telsoa01c577f2c2018-08-31 09:22:23 +0100821 static_cast<float>(desc.m_StrideX)) + 1));
822
823 // Load the weight data
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100824 size_t allWeightsSize = armnn::numeric_cast<size_t>(inputShape.dim(1) * kernelH * kernelW);
telsoa01c577f2c2018-08-31 09:22:23 +0100825 vector<float> weightData(allWeightsSize);
826
827 GetDataFromBlob(layerParam, weightData, 0);
828
829 // depth multiplier will be 1 for the depthwise convolution
830 const unsigned int weightDimSizes[4] = {
831 static_cast<unsigned int>(1), // depth multiplier
832 static_cast<unsigned int>(inputShape.dim(1)), // #channels
833 kernelH,
834 kernelW};
835
836 armnn::IConnectableLayer* returnLayer = nullptr;
837 ConstTensor weights(TensorInfo(4, weightDimSizes, DataType::Float32), weightData.data());
Matteo Martincighfc598e12019-05-14 10:36:13 +0100838 Optional<ConstTensor> optionalBiases;
839 vector<float> biasData;
telsoa01c577f2c2018-08-31 09:22:23 +0100840 if (desc.m_BiasEnabled)
841 {
842 TensorInfo biasInfo;
telsoa01c577f2c2018-08-31 09:22:23 +0100843
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100844 biasData.resize(armnn::numeric_cast<size_t>(outputShape.dim(1)), 1.f);
telsoa01c577f2c2018-08-31 09:22:23 +0100845 GetDataFromBlob(layerParam, biasData, 1);
846
847 const unsigned int biasDimSizes[1] = {static_cast<unsigned int>(outputShape.dim(1))};
848 biasInfo = TensorInfo(1, biasDimSizes, DataType::Float32);
849
850 ConstTensor biases(biasInfo, biasData.data());
Matteo Martincighfc598e12019-05-14 10:36:13 +0100851 optionalBiases = Optional<ConstTensor>(biases);
telsoa01c577f2c2018-08-31 09:22:23 +0100852 }
Matteo Martincighfc598e12019-05-14 10:36:13 +0100853 returnLayer = m_Network->AddDepthwiseConvolution2dLayer(desc,
854 weights,
855 optionalBiases,
856 layerParam.name().c_str());
telsoa014fcda012018-03-09 14:13:49 +0000857
surmeh013537c2c2018-05-18 16:31:43 +0100858 if (!returnLayer)
859 {
telsoa01c577f2c2018-08-31 09:22:23 +0100860 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100861 fmt::format("Failed to create depthwise convolution layer. "
862 "Layer={} #filters={} {}",
863 layerParam.name(),
864 numFilters,
865 CHECK_LOCATION().AsString()));
telsoa01c577f2c2018-08-31 09:22:23 +0100866 }
867 armnn::IOutputSlot& inputConnection = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0));
868 inputConnection.Connect(returnLayer->GetInputSlot(0));
869 returnLayer->GetOutputSlot(0).SetTensorInfo(BlobShapeToTensorInfo(outputShape));
870 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), returnLayer->GetOutputSlot(0));
871}
872
873void CaffeParserBase::ParseConvLayer(const LayerParameter& layerParam)
874{
875 // Ignored Caffe Parameters
telsoa01c577f2c2018-08-31 09:22:23 +0100876 // * Weight Filler
877 // * Bias Filler
878 // * Engine
879 // * Force nd_im2col
880 // * Axis
881
882 // Not Available ArmNN Interface Parameters
883 // * Rounding policy;
884
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100885 ARMNN_ASSERT(layerParam.type() == "Convolution");
telsoa01c577f2c2018-08-31 09:22:23 +0100886 ValidateNumInputsOutputs(layerParam, 1, 1);
887
888 ConvolutionParameter convParam = layerParam.convolution_param();
889 BlobShape inputShape = TensorDescToBlobShape(GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo());
890 const unsigned int numGroups = convParam.has_group() ? convParam.group() : 1;
891 unsigned int numFilters = convParam.num_output();
892
893 const auto notFound = std::numeric_limits<unsigned int>::max();
894
895 unsigned int kernelH = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
896 kernel_h, kernel_size, unsigned int, notFound);
897 unsigned int kernelW = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
898 kernel_w, kernel_size, unsigned int, notFound);
899
900 unsigned int strideH = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
901 stride_h, stride, unsigned int, 1u);
902 unsigned int strideW = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
903 stride_w, stride, unsigned int, 1u);
904
905 unsigned int padH = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
906 pad_h, pad, unsigned int, 0u);
907 unsigned int padW = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
908 pad_w, pad, unsigned int, 0u);
909
Keith Mok7dc18202020-12-20 13:45:51 -0800910 unsigned int dilationH = convParam.dilation_size() > 0 ? convParam.dilation(0) : 1;
911 unsigned int dilationW = convParam.dilation_size() > 1 ? convParam.dilation(1) :
912 convParam.dilation_size() > 0 ? convParam.dilation(0) : 1;
913
telsoa01c577f2c2018-08-31 09:22:23 +0100914 Convolution2dDescriptor convolution2dDescriptor;
915 convolution2dDescriptor.m_PadLeft = padW;
916 convolution2dDescriptor.m_PadRight = padW;
917 convolution2dDescriptor.m_PadTop = padH;
918 convolution2dDescriptor.m_PadBottom = padH;
919 convolution2dDescriptor.m_StrideX = strideW;
920 convolution2dDescriptor.m_StrideY = strideH;
Keith Mok7dc18202020-12-20 13:45:51 -0800921 convolution2dDescriptor.m_DilationX = dilationW;
922 convolution2dDescriptor.m_DilationY = dilationH;
telsoa01c577f2c2018-08-31 09:22:23 +0100923 convolution2dDescriptor.m_BiasEnabled = convParam.has_bias_term() ? convParam.bias_term() : true;
924
925 if (numGroups > numFilters)
926 {
927 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100928 fmt::format("Error parsing Convolution: {}. "
929 "The 'group'={} parameter cannot be larger than the "
930 "number of filters supplied ='{}'. {}",
931 layerParam.name(),
932 numGroups,
933 numFilters,
934 CHECK_LOCATION().AsString()));
telsoa01c577f2c2018-08-31 09:22:23 +0100935 }
936
937 if (inputShape.dim_size() != 4)
938 {
939 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100940 fmt::format("Convolution input shape is expected to have 4 dimensions. "
941 "{}'s input has only {}. {}",
942 layerParam.name(),
943 inputShape.dim_size(),
944 CHECK_LOCATION().AsString()));
telsoa01c577f2c2018-08-31 09:22:23 +0100945 }
946
947 if (numGroups > 1)
948 {
949 if (numGroups > inputShape.dim(1))
950 {
951 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100952 fmt::format("Error parsing Convolution: {}. "
953 "The 'group'={} parameter cannot be larger than the "
954 "channel of the input shape={} (in NCHW format). {}",
955 layerParam.name(),
956 numGroups,
957 inputShape.dim(1),
958 CHECK_LOCATION().AsString()));
telsoa01c577f2c2018-08-31 09:22:23 +0100959 }
960 else if (numGroups == inputShape.dim(1))
961 {
962 // we use a depthwise convolution here, because the number of groups equals to the
963 // input channels
964 AddConvLayerWithDepthwiseConv(layerParam, convolution2dDescriptor, kernelW, kernelH);
965 return;
966 }
967 else
968 {
969 // we split the input by channels into channels/groups separate convolutions
Jim Flynne242f2d2019-05-22 14:24:13 +0100970 // and concatenate the results afterwards
telsoa01c577f2c2018-08-31 09:22:23 +0100971 AddConvLayerWithSplits(layerParam, convolution2dDescriptor, kernelW, kernelH);
972 return;
973 }
974 }
975
976 // NOTE: at this point we only need to handle #group=1 case, all other cases should be
977 // handled by the AddConvLayer* helpers
978
979 // Populate convolution output tensor descriptor dimensions
980 BlobShape outputShape;
981 outputShape.add_dim(0);
982 outputShape.set_dim(0, inputShape.dim(0));
983 outputShape.add_dim(1);
984 outputShape.set_dim(1, numFilters);
985 outputShape.add_dim(2);
986 outputShape.set_dim(
987 2, (static_cast<int>(
Keith Mok7dc18202020-12-20 13:45:51 -0800988 static_cast<float>(inputShape.dim(2) + 2 * padH - (dilationH * (kernelH - 1) + 1)) /
telsoa01c577f2c2018-08-31 09:22:23 +0100989 static_cast<float>(strideH)) + 1));
990 outputShape.add_dim(3);
991 outputShape.set_dim(
992 3, (static_cast<int>(
Keith Mok7dc18202020-12-20 13:45:51 -0800993 static_cast<float>(inputShape.dim(3) + 2 * padW - (dilationW * (kernelW - 1) + 1)) /
telsoa01c577f2c2018-08-31 09:22:23 +0100994 static_cast<float>(strideW)) + 1));
995
996 // Load the weight data for ALL groups
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100997 vector<float> weightData(armnn::numeric_cast<size_t>(inputShape.dim(1) *
telsoa01c577f2c2018-08-31 09:22:23 +0100998 outputShape.dim(1) *
999 kernelH *
1000 kernelW));
1001 GetDataFromBlob(layerParam, weightData, 0);
1002
1003 const unsigned int weightDimSizes[4] = {
1004 static_cast<unsigned int>(outputShape.dim(1)), // output channels
1005 static_cast<unsigned int>(inputShape.dim(1)), // input channels
1006 kernelH,
1007 kernelW};
1008
1009 armnn::IConnectableLayer* returnLayer = nullptr;
1010
1011 // Pull out the weights for this group from that loaded from the model file earlier
1012 ConstTensor weights(TensorInfo(4, weightDimSizes, DataType::Float32), weightData.data());
Matteo Martincighfc598e12019-05-14 10:36:13 +01001013 Optional<ConstTensor> optionalBiases;
1014 vector<float> biasData;
telsoa01c577f2c2018-08-31 09:22:23 +01001015 if (convolution2dDescriptor.m_BiasEnabled)
1016 {
1017 TensorInfo biasInfo;
telsoa01c577f2c2018-08-31 09:22:23 +01001018
Matthew Sloyan589e3e82020-09-11 16:17:48 +01001019 biasData.resize(armnn::numeric_cast<size_t>(outputShape.dim(1)), 1.f);
telsoa01c577f2c2018-08-31 09:22:23 +01001020 GetDataFromBlob(layerParam, biasData, 1);
1021
1022 const unsigned int biasDimSizes[1] = {static_cast<unsigned int>(outputShape.dim(1))};
1023 biasInfo = TensorInfo(1, biasDimSizes, DataType::Float32);
1024
1025 // Pull out the biases for this group from that loaded from the model file earlier
1026 ConstTensor biases(biasInfo, biasData.data());
Matteo Martincighfc598e12019-05-14 10:36:13 +01001027 optionalBiases = Optional<ConstTensor>(biases);
telsoa01c577f2c2018-08-31 09:22:23 +01001028 }
Matteo Martincighfc598e12019-05-14 10:36:13 +01001029 returnLayer = m_Network->AddConvolution2dLayer(convolution2dDescriptor,
1030 weights,
1031 optionalBiases,
1032 layerParam.name().c_str());
telsoa01c577f2c2018-08-31 09:22:23 +01001033
1034 armnn::IOutputSlot& inputConnection = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0));
1035 inputConnection.Connect(returnLayer->GetInputSlot(0));
1036 returnLayer->GetOutputSlot(0).SetTensorInfo(BlobShapeToTensorInfo(outputShape));
1037
1038 if (!returnLayer)
1039 {
1040 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001041 fmt::format("Failed to create Convolution layer. "
1042 "Layer={} #groups={} #filters={} {}",
1043 layerParam.name(),
1044 numGroups,
1045 numFilters,
1046 CHECK_LOCATION().AsString()));
surmeh013537c2c2018-05-18 16:31:43 +01001047 }
1048
telsoa014fcda012018-03-09 14:13:49 +00001049 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), returnLayer->GetOutputSlot(0));
1050}
1051
Keith Mok9801b172020-12-20 19:47:25 -08001052void CaffeParserBase::ParseDeconvLayer(const LayerParameter& layerParam)
1053{
1054 // Ignored Caffe Parameters
1055 // * Weight Filler
1056 // * Bias Filler
1057 // * Engine
1058 // * Force nd_im2col
1059 // * Axis
1060
1061 // Not Available ArmNN Interface Parameters
1062 // * Rounding policy;
1063
1064 ARMNN_ASSERT(layerParam.type() == "Deconvolution");
1065 ValidateNumInputsOutputs(layerParam, 1, 1);
1066
1067 ConvolutionParameter convParam = layerParam.convolution_param();
1068 BlobShape inputShape = TensorDescToBlobShape(GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo());
1069 const unsigned int numGroups = convParam.has_group() ? convParam.group() : 1;
1070 unsigned int numFilters = convParam.num_output();
1071
1072 const auto notFound = std::numeric_limits<unsigned int>::max();
1073
1074 unsigned int kernelH = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
1075 kernel_h, kernel_size, unsigned int, notFound);
1076 unsigned int kernelW = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
1077 kernel_w, kernel_size, unsigned int, notFound);
1078
1079 unsigned int strideH = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
1080 stride_h, stride, unsigned int, 1u);
1081 unsigned int strideW = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
1082 stride_w, stride, unsigned int, 1u);
1083
1084 unsigned int padH = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
1085 pad_h, pad, unsigned int, 0u);
1086 unsigned int padW = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
1087 pad_w, pad, unsigned int, 0u);
1088
1089 unsigned int dilationH = convParam.dilation_size() > 0 ? convParam.dilation(0) : 1;
1090 unsigned int dilationW = convParam.dilation_size() > 1 ? convParam.dilation(1) :
1091 convParam.dilation_size() > 0 ? convParam.dilation(0) : 1;
1092
1093 if (dilationH != 1 || dilationW != 1) {
1094 fmt::format("Dilated decnvolution is not supported. "
1095 "{}'s input has dilation {} {}. {}",
1096 layerParam.name(),
1097 dilationW, dilationH,
1098 CHECK_LOCATION().AsString());
1099 }
1100
1101 TransposeConvolution2dDescriptor deconvolution2dDescriptor;
1102 deconvolution2dDescriptor.m_PadLeft = padW;
1103 deconvolution2dDescriptor.m_PadRight = padW;
1104 deconvolution2dDescriptor.m_PadTop = padH;
1105 deconvolution2dDescriptor.m_PadBottom = padH;
1106 deconvolution2dDescriptor.m_StrideX = strideW;
1107 deconvolution2dDescriptor.m_StrideY = strideH;
1108 deconvolution2dDescriptor.m_BiasEnabled = convParam.has_bias_term() ? convParam.bias_term() : true;
1109
1110 if (numGroups > numFilters)
1111 {
1112 throw ParseException(
1113 fmt::format("Error parsing Deconvolution: {}. "
1114 "The 'group'={} parameter cannot be larger than the "
1115 "number of filters supplied ='{}'. {}",
1116 layerParam.name(),
1117 numGroups,
1118 numFilters,
1119 CHECK_LOCATION().AsString()));
1120 }
1121
1122 if (inputShape.dim_size() != 4)
1123 {
1124 throw ParseException(
1125 fmt::format("Deconvolution input shape is expected to have 4 dimensions. "
1126 "{}'s input has only {}. {}",
1127 layerParam.name(),
1128 inputShape.dim_size(),
1129 CHECK_LOCATION().AsString()));
1130 }
1131
1132 if (numGroups > 1)
1133 {
1134 if (numGroups > inputShape.dim(1))
1135 {
1136 throw ParseException(
1137 fmt::format("Error parsing Deconvolution: {}. "
1138 "The 'group'={} parameter cannot be larger than the "
1139 "channel of the input shape={} (in NCHW format). {}",
1140 layerParam.name(),
1141 numGroups,
1142 inputShape.dim(1),
1143 CHECK_LOCATION().AsString()));
1144 }
1145 else
1146 {
1147 // we split the input by channels into channels/groups separate convolutions
1148 // and concatenate the results afterwards
1149 AddDeconvLayerWithSplits(layerParam, deconvolution2dDescriptor, kernelW, kernelH);
1150 return;
1151 }
1152 }
1153
1154 // NOTE: at this point we only need to handle #group=1 case, all other cases should be
1155 // handled by the AddDeconvLayer* helpers
1156
1157 // Populate deconvolution output tensor descriptor dimensions
1158 BlobShape outputShape;
1159 outputShape.add_dim(0);
1160 outputShape.set_dim(0, inputShape.dim(0));
1161 outputShape.add_dim(1);
1162 outputShape.set_dim(1, numFilters);
1163 outputShape.add_dim(2);
1164 outputShape.set_dim(
1165 2, (static_cast<int>(
1166 strideH * (inputShape.dim(2) - 1) - 2 * padH + (dilationH * (kernelH - 1) + 1))));
1167 outputShape.add_dim(3);
1168 outputShape.set_dim(
1169 3, (static_cast<int>(
1170 strideW * (inputShape.dim(3) - 1) - 2 * padW + (dilationW * (kernelW - 1) + 1))));
1171
1172 // Load the weight data for ALL groups
1173 vector<float> weightData(armnn::numeric_cast<size_t>(inputShape.dim(1) *
1174 outputShape.dim(1) *
1175 kernelH *
1176 kernelW));
1177 GetDataFromBlob(layerParam, weightData, 0);
1178
1179 const unsigned int weightDimSizes[4] = {
1180 static_cast<unsigned int>(outputShape.dim(1)), // output channels
1181 static_cast<unsigned int>(inputShape.dim(1)), // input channels
1182 kernelH,
1183 kernelW};
1184
1185 armnn::IConnectableLayer* returnLayer = nullptr;
1186
1187 // Pull out the weights for this group from that loaded from the model file earlier
1188 ConstTensor weights(TensorInfo(4, weightDimSizes, DataType::Float32), weightData.data());
1189 Optional<ConstTensor> optionalBiases;
1190 vector<float> biasData;
1191 if (deconvolution2dDescriptor.m_BiasEnabled)
1192 {
1193 TensorInfo biasInfo;
1194
1195 biasData.resize(armnn::numeric_cast<size_t>(outputShape.dim(1)), 1.f);
1196 GetDataFromBlob(layerParam, biasData, 1);
1197
1198 const unsigned int biasDimSizes[1] = {static_cast<unsigned int>(outputShape.dim(1))};
1199 biasInfo = TensorInfo(1, biasDimSizes, DataType::Float32);
1200
1201 // Pull out the biases for this group from that loaded from the model file earlier
1202 ConstTensor biases(biasInfo, biasData.data());
1203 optionalBiases = Optional<ConstTensor>(biases);
1204 }
1205 returnLayer = m_Network->AddTransposeConvolution2dLayer(deconvolution2dDescriptor,
1206 weights,
1207 optionalBiases,
1208 layerParam.name().c_str());
1209
1210 armnn::IOutputSlot& inputConnection = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0));
1211 inputConnection.Connect(returnLayer->GetInputSlot(0));
1212 returnLayer->GetOutputSlot(0).SetTensorInfo(BlobShapeToTensorInfo(outputShape));
1213
1214 if (!returnLayer)
1215 {
1216 throw ParseException(
1217 fmt::format("Failed to create Deconvolution layer. "
1218 "Layer={} #groups={} #filters={} {}",
1219 layerParam.name(),
1220 numGroups,
1221 numFilters,
1222 CHECK_LOCATION().AsString()));
1223 }
1224
1225 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), returnLayer->GetOutputSlot(0));
1226}
1227
telsoa01c577f2c2018-08-31 09:22:23 +01001228void CaffeParserBase::ParsePoolingLayer(const LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +00001229{
telsoa01c577f2c2018-08-31 09:22:23 +01001230 // Ignored Caffe Parameters
1231 // Stochastic Pooling
1232 // Engine
1233
telsoa014fcda012018-03-09 14:13:49 +00001234 ValidateNumInputsOutputs(layerParam, 1, 1);
telsoa014fcda012018-03-09 14:13:49 +00001235 PoolingParameter param = layerParam.pooling_param();
telsoa014fcda012018-03-09 14:13:49 +00001236 const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo();
1237
telsoa01c577f2c2018-08-31 09:22:23 +01001238 const auto notFound = std::numeric_limits<unsigned int>::max();
1239
1240 unsigned int kernel_h = GET_OPTIONAL_WITH_FALLBACK(param, PoolingParameter,
1241 kernel_h, kernel_size, unsigned int, notFound);
1242 unsigned int kernel_w = GET_OPTIONAL_WITH_FALLBACK(param, PoolingParameter,
1243 kernel_w, kernel_size, unsigned int, notFound);
1244
1245 if ((kernel_h == notFound || kernel_w == notFound) && param.has_global_pooling())
telsoa014fcda012018-03-09 14:13:49 +00001246 {
1247 kernel_h = inputInfo.GetShape()[2];
1248 kernel_w = inputInfo.GetShape()[3];
1249 }
telsoa01c577f2c2018-08-31 09:22:23 +01001250
telsoa01c577f2c2018-08-31 09:22:23 +01001251 unsigned int stride_h = GET_OPTIONAL_WITH_FALLBACK(param, PoolingParameter,
1252 stride_h, stride, unsigned int, notFound);
1253 unsigned int stride_w = GET_OPTIONAL_WITH_FALLBACK(param, PoolingParameter,
1254 stride_h, stride, unsigned int, notFound);
1255
1256 if ((stride_h == notFound || stride_w == notFound) && param.has_global_pooling())
telsoa014fcda012018-03-09 14:13:49 +00001257 {
telsoa01c577f2c2018-08-31 09:22:23 +01001258 stride_h = 1;
1259 stride_w = 1;
telsoa014fcda012018-03-09 14:13:49 +00001260 }
1261
telsoa01c577f2c2018-08-31 09:22:23 +01001262 unsigned int pad_h = GET_OPTIONAL_WITH_FALLBACK(param, PoolingParameter,
1263 pad_h, pad, unsigned int, 0u);
1264 unsigned int pad_w = GET_OPTIONAL_WITH_FALLBACK(param, PoolingParameter,
1265 pad_w, pad, unsigned int, 0u);
telsoa014fcda012018-03-09 14:13:49 +00001266
telsoa014fcda012018-03-09 14:13:49 +00001267 // Populate Weight and Bias Filter Descriptor
1268 Pooling2dDescriptor pooling2dDescriptor;
1269 if (param.has_pool())
1270 {
1271 PoolingParameter_PoolMethod p = param.pool();
1272 switch (p)
1273 {
1274 case PoolingParameter_PoolMethod_MAX:
1275 {
1276 pooling2dDescriptor.m_PoolType = PoolingAlgorithm::Max;
1277 break;
1278 }
1279 case PoolingParameter_PoolMethod_AVE:
1280 {
1281 pooling2dDescriptor.m_PoolType = PoolingAlgorithm::Average;
1282 break;
1283 }
1284 case PoolingParameter_PoolMethod_STOCHASTIC:
1285 {
telsoa01c577f2c2018-08-31 09:22:23 +01001286 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001287 fmt::format("Pooling Layer: Stochastic Pooling Not Supported. Layer={} {}",
1288 layerParam.name(),
1289 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001290 }
1291 default:
1292 {
telsoa01c577f2c2018-08-31 09:22:23 +01001293 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001294 fmt::format("Pooling Layer: unknown pooling method: {} for layer: {} {}",
1295 p,
1296 layerParam.name(),
1297 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001298 }
1299 }
1300 }
1301 else
1302 {
telsoa01c577f2c2018-08-31 09:22:23 +01001303 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001304 fmt::format("No Pooling Method Defined for {} {}",
1305 layerParam.name(),
1306 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001307 }
1308
1309 pooling2dDescriptor.m_PadLeft = pad_w;
1310 pooling2dDescriptor.m_PadRight = pad_w;
1311 pooling2dDescriptor.m_PadTop = pad_h;
1312 pooling2dDescriptor.m_PadBottom = pad_h;
1313 pooling2dDescriptor.m_StrideX = stride_w;
1314 pooling2dDescriptor.m_StrideY = stride_h;
1315 pooling2dDescriptor.m_PoolWidth = kernel_w;
1316 pooling2dDescriptor.m_PoolHeight = kernel_h;
1317
1318 pooling2dDescriptor.m_OutputShapeRounding = OutputShapeRounding::Ceiling;
1319 pooling2dDescriptor.m_PaddingMethod = PaddingMethod::IgnoreValue;
1320
1321 armnn::IConnectableLayer* poolingLayer = m_Network->AddPooling2dLayer(pooling2dDescriptor,
1322 layerParam.name().c_str());
1323
telsoa014fcda012018-03-09 14:13:49 +00001324 TensorInfo outputInfo(
1325 { inputInfo.GetShape()[0],
1326 inputInfo.GetShape()[1],
1327 static_cast<unsigned int>(ceil(
1328 static_cast<float>(inputInfo.GetShape()[2] + 2 * pad_h - kernel_h) /
Matthew Sloyan24ac8592020-09-23 16:57:23 +01001329 armnn::numeric_cast<float>(stride_h))) + 1,
telsoa014fcda012018-03-09 14:13:49 +00001330 static_cast<unsigned int>(ceil(
1331 static_cast<float>(inputInfo.GetShape()[3] + 2 * pad_w - kernel_w) /
Matthew Sloyan24ac8592020-09-23 16:57:23 +01001332 armnn::numeric_cast<float>(stride_w))) + 1 },
telsoa014fcda012018-03-09 14:13:49 +00001333 DataType::Float32);
1334
1335 GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).Connect(poolingLayer->GetInputSlot(0));
1336 poolingLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1337 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), poolingLayer->GetOutputSlot(0));
1338}
1339
Keith Mok9801b172020-12-20 19:47:25 -08001340void CaffeParserBase::ParseArgmaxLayer(const LayerParameter& layerParam)
1341{
1342 ValidateNumInputsOutputs(layerParam, 1, 1);
1343 ArgMaxParameter param = layerParam.argmax_param();
1344
1345 BlobShape inputShape = TensorDescToBlobShape(GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo());
1346
1347 const unsigned int topK = param.has_top_k() ? param.top_k() : 1;
1348 if (topK != 1) {
1349 throw ParseException(
1350 fmt::format("ArgMaxLayer: Only support top_k equals to 1. Layer={} {}",
1351 layerParam.name(),
1352 CHECK_LOCATION().AsString()));
1353 }
1354
1355 const unsigned int outMaxVal = param.has_out_max_val() ? param.out_max_val() : false;
1356 if (outMaxVal) {
1357 throw ParseException(
1358 fmt::format("ArgMaxLayer: Does not support out_max_val. Layer={} {}",
1359 layerParam.name(),
1360 CHECK_LOCATION().AsString()));
1361 }
1362
1363 int axis = param.has_axis() ? param.axis() : 1;
1364 if (axis < 0) {
1365 axis = inputShape.dim_size() - axis;
1366 }
1367 if ((axis < 0) || (axis >= inputShape.dim_size())) {
1368 throw ParseException(
1369 fmt::format("ArgMaxLayer: Invalid axis value which outside range of input dims. "
1370 "{}'s input has input dim_size {}, requested axis: {}. {}",
1371 layerParam.name(),
1372 inputShape.dim_size(),
1373 axis,
1374 CHECK_LOCATION().AsString()));
1375 }
1376
1377 ArgMinMaxDescriptor desc;
1378 desc.m_Axis = axis;
1379 desc.m_Output_Type = armnn::DataType::Signed32;
1380 desc.m_Function = ArgMinMaxFunction::Max;
1381
1382 armnn::IConnectableLayer* argmaxLayer = m_Network->AddArgMinMaxLayer(desc,
1383 layerParam.name().c_str());
1384
1385 TensorShape outputShape(static_cast<unsigned int>(inputShape.dim_size() - 1));
1386 int j = 0;
1387 // remove the flatten axis
1388 for (int i = 0; i < inputShape.dim_size(); ++i)
1389 {
1390 if (i == axis) continue;
1391 outputShape[static_cast<unsigned int>(j++)] = static_cast<unsigned int>(inputShape.dim(i));
1392 }
1393 TensorInfo outputInfo(outputShape, DataType::Signed32);
1394
1395 GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).Connect(argmaxLayer->GetInputSlot(0));
1396 argmaxLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1397 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), argmaxLayer->GetOutputSlot(0));
1398}
1399
telsoa01c577f2c2018-08-31 09:22:23 +01001400void CaffeParserBase::ParseReluLayer(const LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +00001401{
1402 ValidateNumInputsOutputs(layerParam, 1, 1);
1403
1404 const string& name = layerParam.name();
1405 const ReLUParameter& param = layerParam.relu_param();
1406
1407 ActivationDescriptor activationDescriptor;
1408 const float negativeSlope = param.negative_slope();
1409 if (negativeSlope == 0.0f)
1410 {
1411 activationDescriptor.m_Function = ActivationFunction::ReLu;
1412 }
1413 else
1414 {
1415 activationDescriptor.m_Function = ActivationFunction::LeakyReLu;
1416 activationDescriptor.m_A = negativeSlope;
1417 }
1418
1419 const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo();
1420 IConnectableLayer* const activationLayer = m_Network->AddActivationLayer(activationDescriptor, name.c_str());
1421 GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).Connect(activationLayer->GetInputSlot(0));
1422 activationLayer->GetOutputSlot(0).SetTensorInfo(inputInfo);
1423 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), activationLayer->GetOutputSlot(0));
1424}
1425
telsoa01c577f2c2018-08-31 09:22:23 +01001426void CaffeParserBase::ParseLRNLayer(const LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +00001427{
1428 ValidateNumInputsOutputs(layerParam, 1, 1);
1429
1430 LRNParameter param = layerParam.lrn_param();
1431
1432 const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo();
1433
telsoa01c577f2c2018-08-31 09:22:23 +01001434 // Ignored BATCH NORMALIZATION Caffe Parameters.
1435 // Ignored MVN Caffe Parameters.
1436 // Ignored LRN Caffe Parameters.
telsoa014fcda012018-03-09 14:13:49 +00001437 // Engine
1438
1439 NormalizationDescriptor normalizationDescriptor;
1440 if (param.has_norm_region())
1441 {
1442 LRNParameter_NormRegion n = param.norm_region();
1443 switch (n)
1444 {
1445 case LRNParameter_NormRegion_ACROSS_CHANNELS:
1446 {
1447 normalizationDescriptor.m_NormChannelType = NormalizationAlgorithmChannel::Across;
1448 break;
1449 }
1450 case LRNParameter_NormRegion_WITHIN_CHANNEL:
1451 {
1452 normalizationDescriptor.m_NormChannelType = NormalizationAlgorithmChannel::Within;
1453 break;
1454 }
1455 default:
telsoa01c577f2c2018-08-31 09:22:23 +01001456 {
1457 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001458 fmt::format("Unknown region {} for LRN layer {} {}",
1459 n,
1460 layerParam.name(),
1461 CHECK_LOCATION().AsString()));
telsoa01c577f2c2018-08-31 09:22:23 +01001462 }
telsoa014fcda012018-03-09 14:13:49 +00001463 }
1464 }
1465 else
1466 {
telsoa01c577f2c2018-08-31 09:22:23 +01001467 // Caffe defaults to normalization across channels.
telsoa014fcda012018-03-09 14:13:49 +00001468 normalizationDescriptor.m_NormChannelType = NormalizationAlgorithmChannel::Across;
1469 }
1470
1471 normalizationDescriptor.m_NormMethodType = NormalizationAlgorithmMethod::LocalBrightness;
1472 if (param.has_local_size())
1473 {
1474 normalizationDescriptor.m_NormSize = param.local_size();
1475 }
1476 else
1477 {
telsoa01c577f2c2018-08-31 09:22:23 +01001478 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001479 fmt::format("local_size not defined for LRN layer {} {}",
1480 layerParam.name(),
1481 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001482 }
1483
1484 if (param.has_alpha())
1485 {
1486 normalizationDescriptor.m_Alpha = param.alpha();
Matthew Sloyan24ac8592020-09-23 16:57:23 +01001487 normalizationDescriptor.m_Alpha /= armnn::numeric_cast<float>(param.local_size());
telsoa014fcda012018-03-09 14:13:49 +00001488 }
1489 else
1490 {
telsoa01c577f2c2018-08-31 09:22:23 +01001491 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001492 fmt::format("Alpha not defined for LRN layer {} {}",
1493 layerParam.name(),
1494 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001495 }
1496 if (param.has_beta())
1497 {
1498 normalizationDescriptor.m_Beta = param.beta();
1499 }
1500 else
1501 {
telsoa01c577f2c2018-08-31 09:22:23 +01001502 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001503 fmt::format("Beta not defined for LRN layer {} {}",
1504 layerParam.name(),
1505 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001506 }
telsoa01c577f2c2018-08-31 09:22:23 +01001507
telsoa014fcda012018-03-09 14:13:49 +00001508 if (param.has_k())
1509 {
1510 normalizationDescriptor.m_K = param.k();
1511 }
1512 else
telsoa01c577f2c2018-08-31 09:22:23 +01001513 {
telsoa014fcda012018-03-09 14:13:49 +00001514 normalizationDescriptor.m_K = 1;
telsoa01c577f2c2018-08-31 09:22:23 +01001515 }
telsoa014fcda012018-03-09 14:13:49 +00001516
1517 IConnectableLayer* const normLayer = m_Network->AddNormalizationLayer(normalizationDescriptor,
1518 layerParam.name().c_str());
1519 GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).Connect(normLayer->GetInputSlot(0));
1520 normLayer->GetOutputSlot(0).SetTensorInfo(inputInfo);
1521
1522 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), normLayer->GetOutputSlot(0));
1523}
1524
telsoa01c577f2c2018-08-31 09:22:23 +01001525void CaffeParserBase::ParseInnerProductLayer(const LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +00001526{
1527 InnerProductParameter param = layerParam.inner_product_param();
1528
1529 ValidateNumInputsOutputs(layerParam, 1, 1);
1530
1531 unsigned int outputSize = param.num_output();
1532
telsoa01c577f2c2018-08-31 09:22:23 +01001533 // Ignored Caffe Parameters:
telsoa014fcda012018-03-09 14:13:49 +00001534 // Weight Filler
1535 // Bias Filler
1536 // Engine
1537 // Axis
1538
1539 FullyConnectedDescriptor tensorFullyConnectedDescriptor;
1540
1541 if (param.has_transpose())
1542 {
telsoa01c577f2c2018-08-31 09:22:23 +01001543 // If true, assumes transposed weights.
telsoa014fcda012018-03-09 14:13:49 +00001544 tensorFullyConnectedDescriptor.m_TransposeWeightMatrix = param.transpose();
1545 }
1546 else
1547 {
telsoa01c577f2c2018-08-31 09:22:23 +01001548 // Caffe defaults to transposed.
telsoa014fcda012018-03-09 14:13:49 +00001549 tensorFullyConnectedDescriptor.m_TransposeWeightMatrix = true;
1550 }
1551
1552 const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo();
1553
1554 TensorInfo weightInfo;
1555 TensorInfo biasInfo;
1556
telsoa01c577f2c2018-08-31 09:22:23 +01001557 // Allows implicit flattening of extra dimensions.
telsoa014fcda012018-03-09 14:13:49 +00001558 unsigned int inputSize = inputInfo.GetShape()[1];
1559 for (unsigned int i = 2; i < inputInfo.GetNumDimensions(); ++i)
1560 {
1561 inputSize *= inputInfo.GetShape()[i];
1562 }
1563
telsoa01c577f2c2018-08-31 09:22:23 +01001564 const float* weightDataPtr = GetArrayPtrFromBlob(layerParam, 0);
telsoa014fcda012018-03-09 14:13:49 +00001565 const unsigned int swTD[2] = { outputSize, inputSize };
telsoa01c577f2c2018-08-31 09:22:23 +01001566 ConstTensor weights(TensorInfo(2, swTD, DataType::Float32), weightDataPtr);
telsoa014fcda012018-03-09 14:13:49 +00001567
1568 tensorFullyConnectedDescriptor.m_BiasEnabled = true;
telsoa01c577f2c2018-08-31 09:22:23 +01001569 // Todo: check whether bias enabled.
telsoa014fcda012018-03-09 14:13:49 +00001570 armnn::IConnectableLayer* fullyConnectedLayer = nullptr;
1571 if (tensorFullyConnectedDescriptor.m_BiasEnabled)
1572 {
1573 // BIAS VALUE
telsoa01c577f2c2018-08-31 09:22:23 +01001574 const float* biasDataPtr = GetArrayPtrFromBlob(layerParam, 1);
telsoa014fcda012018-03-09 14:13:49 +00001575
1576 const unsigned int sbTD[1] = { outputSize };
1577
telsoa01c577f2c2018-08-31 09:22:23 +01001578 ConstTensor biases(TensorInfo(1, sbTD, DataType::Float32), biasDataPtr);
telsoa014fcda012018-03-09 14:13:49 +00001579
Matteo Martincighfc598e12019-05-14 10:36:13 +01001580 fullyConnectedLayer = m_Network->AddFullyConnectedLayer(tensorFullyConnectedDescriptor,
1581 weights,
1582 Optional<ConstTensor>(biases),
1583 layerParam.name().c_str());
telsoa014fcda012018-03-09 14:13:49 +00001584 }
1585 else
1586 {
Matteo Martincighfc598e12019-05-14 10:36:13 +01001587 fullyConnectedLayer = m_Network->AddFullyConnectedLayer(tensorFullyConnectedDescriptor,
1588 weights,
1589 EmptyOptional(),
1590 layerParam.name().c_str());
telsoa014fcda012018-03-09 14:13:49 +00001591 }
1592
1593 TensorInfo outputInfo({ inputInfo.GetShape()[0], outputSize }, DataType::Float32);
1594 GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).Connect(fullyConnectedLayer->GetInputSlot(0));
1595 fullyConnectedLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1596 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), fullyConnectedLayer->GetOutputSlot(0));
1597}
1598
telsoa01c577f2c2018-08-31 09:22:23 +01001599void CaffeParserBase::ParseSoftmaxLayer(const LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +00001600{
1601 ValidateNumInputsOutputs(layerParam, 1, 1);
1602
1603 SoftmaxParameter param = layerParam.softmax_param();
1604
1605 const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo();
1606
telsoa01c577f2c2018-08-31 09:22:23 +01001607 // Ignored Caffe Parameters:
telsoa014fcda012018-03-09 14:13:49 +00001608 // axis
1609 // Engine
1610
1611 armnn::SoftmaxDescriptor softmaxDescriptor;
Teresa Charlin4320c922020-08-12 16:04:41 +01001612 softmaxDescriptor.m_Axis = 1;
telsoa014fcda012018-03-09 14:13:49 +00001613 armnn::IConnectableLayer* const softmaxLayer = m_Network->AddSoftmaxLayer(
1614 softmaxDescriptor,
1615 layerParam.name().c_str());
1616 GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).Connect(softmaxLayer->GetInputSlot(0));
1617 softmaxLayer->GetOutputSlot(0).SetTensorInfo(inputInfo);
1618 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), softmaxLayer->GetOutputSlot(0));
1619}
1620
telsoa01c577f2c2018-08-31 09:22:23 +01001621void CaffeParserBase::ParseEltwiseLayer(const LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +00001622{
1623 ValidateNumInputsOutputs(layerParam, 2, 1);
1624
1625 const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo();
1626
telsoa01c577f2c2018-08-31 09:22:23 +01001627 // Ignored Caffe Parameters:
telsoa014fcda012018-03-09 14:13:49 +00001628 // coeff
1629
telsoa01c577f2c2018-08-31 09:22:23 +01001630 EltwiseParameter_EltwiseOp operation = EltwiseParameter_EltwiseOp_SUM; // Defaults to sum as per caffe.
telsoa014fcda012018-03-09 14:13:49 +00001631
1632 if (layerParam.has_eltwise_param() && layerParam.eltwise_param().has_operation())
1633 {
1634 operation = layerParam.eltwise_param().operation();
1635 }
1636
1637 armnn::IConnectableLayer* newLayer = nullptr;
1638 switch (operation)
1639 {
1640 case EltwiseParameter_EltwiseOp_SUM:
1641 {
1642 newLayer = m_Network->AddAdditionLayer(layerParam.name().c_str());
1643 break;
1644 }
1645 case EltwiseParameter_EltwiseOp_PROD:
1646 {
1647 newLayer = m_Network->AddMultiplicationLayer(layerParam.name().c_str());
1648 break;
1649 }
1650 default:
1651 {
telsoa01c577f2c2018-08-31 09:22:23 +01001652 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001653 fmt::format("Unsupported operation {} in Eltwise layer {} {}",
1654 operation,
1655 layerParam.name(),
1656 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001657 }
1658 }
1659
1660 GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).Connect(newLayer->GetInputSlot(0));
1661 GetArmnnOutputSlotForCaffeTop(layerParam.bottom(1)).Connect(newLayer->GetInputSlot(1));
1662 newLayer->GetOutputSlot(0).SetTensorInfo(inputInfo);
1663 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), newLayer->GetOutputSlot(0));
1664}
1665
telsoa01c577f2c2018-08-31 09:22:23 +01001666void CaffeParserBase::ParseConcatLayer(const LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +00001667{
1668 unsigned int numInputs = static_cast<unsigned int>(layerParam.bottom_size());
telsoa01c577f2c2018-08-31 09:22:23 +01001669 // We assume concat happens along the channel dimension, which is 1 in (0, 1, 2, 3).
telsoa014fcda012018-03-09 14:13:49 +00001670 unsigned int concatDim = 1;
1671 unsigned int numOfDims = 4;
1672
telsoa01c577f2c2018-08-31 09:22:23 +01001673 // we only consider 4-D tensor here
1674 OriginsDescriptor concatDescriptor(static_cast<uint32_t>(numInputs), numOfDims);
telsoa014fcda012018-03-09 14:13:49 +00001675 std::vector<unsigned int>mergeDimSizes(numOfDims, 0u);
1676
1677 unsigned int mergeDim = 0;
1678 for (unsigned int viewIndex = 0; viewIndex < numInputs; ++viewIndex)
1679 {
1680 const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(
Matthew Sloyan589e3e82020-09-11 16:17:48 +01001681 layerParam.bottom(armnn::numeric_cast<int>(viewIndex))).GetTensorInfo();
telsoa01c577f2c2018-08-31 09:22:23 +01001682 // Checks whether the dimensions of the input tensors are actually 4.
telsoa014fcda012018-03-09 14:13:49 +00001683 if (inputInfo.GetNumDimensions()!=4)
1684 {
telsoa01c577f2c2018-08-31 09:22:23 +01001685 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001686 fmt::format("The number of dimensions for input tensors of "
1687 "the concatenation op should be 4. Inputs of {} has "
1688 "{} dimensions. {}",
1689 layerParam.name(),
1690 inputInfo.GetNumDimensions(),
1691 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001692 }
1693
1694 mergeDimSizes[0] = inputInfo.GetShape()[0];
1695 mergeDimSizes[1] = inputInfo.GetShape()[1];
1696 mergeDimSizes[2] = inputInfo.GetShape()[2];
1697 mergeDimSizes[3] = inputInfo.GetShape()[3];
1698
1699 for (unsigned int j = 0; j < concatDim; ++j)
1700 {
1701 concatDescriptor.SetViewOriginCoord(viewIndex, j, 0);
1702 }
1703
1704 concatDescriptor.SetViewOriginCoord(viewIndex, concatDim, mergeDim);
1705 mergeDim += mergeDimSizes[concatDim];
1706
1707 for (unsigned int j = concatDim+1; j < numOfDims; ++j)
1708 {
1709 concatDescriptor.SetViewOriginCoord(viewIndex, j, 0);
1710 }
1711 }
1712 mergeDimSizes[concatDim] = mergeDim;
1713
Jim Flynn906f9462019-05-10 13:55:21 +01001714 armnn::IConnectableLayer* concatlayer = m_Network->AddConcatLayer(concatDescriptor, layerParam.name().c_str());
telsoa014fcda012018-03-09 14:13:49 +00001715 for (unsigned int i = 0; i < numInputs; ++i)
1716 {
Matthew Sloyan589e3e82020-09-11 16:17:48 +01001717 armnn::IOutputSlot& outputSlot = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(armnn::numeric_cast<int>(i)));
telsoa014fcda012018-03-09 14:13:49 +00001718 outputSlot.Connect(concatlayer->GetInputSlot(i));
1719 }
1720
1721 concatlayer->GetOutputSlot(0).SetTensorInfo(armnn::TensorInfo(numOfDims, mergeDimSizes.data(), DataType::Float32));
1722 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), concatlayer->GetOutputSlot(0));
1723}
1724
telsoa01c577f2c2018-08-31 09:22:23 +01001725void CaffeParserBase::ParseBatchNormLayer(const LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +00001726{
1727 ValidateNumInputsOutputs(layerParam, 1, 1);
1728
1729 const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo();
1730
1731 string name = layerParam.name();
1732
1733 BatchNormParameter param = layerParam.batch_norm_param();
1734 // If use_global_stats is not explicitly set in the model, assume it to be true (its default value
1735 // when the network is in the testing phase).
1736 if (param.has_use_global_stats())
1737 {
1738 if (!param.use_global_stats())
1739 {
telsoa01c577f2c2018-08-31 09:22:23 +01001740 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001741 fmt::format("Error parsing Batch Norm layer '{}': "
1742 "Parameter 'use_global_stats' is set to false, which is "
1743 "unsupported (value used for training). {}",
1744 name,
1745 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001746 }
1747 }
1748
1749 BatchNormalizationDescriptor desc;
1750 desc.m_Eps = param.eps();
1751
1752 unsigned int channels = inputInfo.GetShape()[1];
1753 unsigned int shape[] = {channels};
1754
1755 vector<float> meanData(channels);
1756 GetDataFromBlob(layerParam, meanData, 0);
1757
1758 vector<float> varianceData(channels);
1759 GetDataFromBlob(layerParam, varianceData, 1);
1760
telsoa01c577f2c2018-08-31 09:22:23 +01001761 // Reads moving average factor and applies scaling (if required).
Matthew Sloyan589e3e82020-09-11 16:17:48 +01001762 const BlobProto& blob = layerParam.blobs(armnn::numeric_cast<int>(2));
1763 const float movingAverageFactor = blob.data(armnn::numeric_cast<int>(0));
surmeh013537c2c2018-05-18 16:31:43 +01001764 if(movingAverageFactor != 0.0f)
1765 {
1766 const float scaleFactor = 1.0f / movingAverageFactor;
1767 auto scaleFunction = [scaleFactor](float f) -> float { return f * scaleFactor; };
1768
1769 std::transform(varianceData.begin(), varianceData.end(), varianceData.begin(), scaleFunction);
1770 std::transform(meanData.begin(), meanData.end(), meanData.begin(), scaleFunction);
1771 }
1772
telsoa01c577f2c2018-08-31 09:22:23 +01001773 // Identifies scale operation.
telsoa014fcda012018-03-09 14:13:49 +00001774 vector<float> betaData(channels, 0.0f);
1775 vector<float> gammaData(channels, 1.0f);
1776
1777 ConstTensor mean(TensorInfo(1, shape, armnn::DataType::Float32), meanData);
1778 ConstTensor variance(TensorInfo(1, shape, armnn::DataType::Float32), varianceData);
1779 ConstTensor beta(TensorInfo(1, shape, armnn::DataType::Float32), betaData);
1780 ConstTensor gamma(TensorInfo(1, shape, armnn::DataType::Float32), gammaData);
1781
1782 armnn::IConnectableLayer* const batchNormLayer = m_Network->AddBatchNormalizationLayer(desc,
1783 mean, variance, beta, gamma, name.c_str());
1784 GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).Connect(batchNormLayer->GetInputSlot(0));
1785 batchNormLayer->GetOutputSlot(0).SetTensorInfo(inputInfo);
1786 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), batchNormLayer->GetOutputSlot(0));
1787}
1788
telsoa01c577f2c2018-08-31 09:22:23 +01001789void CaffeParserBase::ParseScaleLayer(const LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +00001790{
telsoa01c577f2c2018-08-31 09:22:23 +01001791 // Current unoptimal solution: add a batchnormalization layer with 0 mean and 1 variance.
telsoa014fcda012018-03-09 14:13:49 +00001792 ValidateNumInputsOutputs(layerParam, 1, 1);
1793
1794 const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo();
1795
1796 string name = layerParam.name();
1797
1798 ScaleParameter param = layerParam.scale_param();
1799 if (param.axis() != 1)
1800 {
1801 // Would have to use something other than BatchNormalizationLayer in this case
telsoa01c577f2c2018-08-31 09:22:23 +01001802 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001803 fmt::format("Loading Scale Layer: Only axis 1 is supported currently. "
1804 "Layer={} Axis={} {}",
1805 layerParam.name(),
1806 param.axis(),
1807 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001808 }
1809
1810 unsigned int channels = inputInfo.GetShape()[1];
1811 unsigned int shape[] = {channels};
1812
1813 BatchNormalizationDescriptor desc;
telsoa01c577f2c2018-08-31 09:22:23 +01001814 desc.m_Eps = 0.0f; // Don't need epsilon if variance is 1.
telsoa014fcda012018-03-09 14:13:49 +00001815 vector<float> meanData(channels, 0.0f);
1816 vector<float> varianceData(channels, 1.0f);
1817 vector<float> betaData(channels, 0.0f);
1818 vector<float> gammaData(channels);
1819
1820 GetDataFromBlob(layerParam, gammaData, 0);
1821
1822 if(param.has_bias_term())
1823 {
1824 GetDataFromBlob(layerParam, betaData, 1);
1825 }
1826
1827 ConstTensor mean(TensorInfo(1, shape, armnn::DataType::Float32), meanData);
1828 ConstTensor variance(TensorInfo(1, shape, armnn::DataType::Float32), varianceData);
1829 ConstTensor beta(TensorInfo(1, shape, armnn::DataType::Float32), betaData);
1830 ConstTensor gamma(TensorInfo(1, shape, armnn::DataType::Float32), gammaData);
1831
1832 armnn::IConnectableLayer* const batchNormLayer = m_Network->AddBatchNormalizationLayer(desc,
1833 mean, variance, beta, gamma, name.c_str());
1834 GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).Connect(batchNormLayer->GetInputSlot(0));
1835 batchNormLayer->GetOutputSlot(0).SetTensorInfo(inputInfo);
1836 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), batchNormLayer->GetOutputSlot(0));
1837}
1838
telsoa01c577f2c2018-08-31 09:22:23 +01001839void CaffeParserBase::ParseSplitLayer(const caffe::LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +00001840{
telsoa01c577f2c2018-08-31 09:22:23 +01001841 // Used in caffe to duplicate memory - not necessary in armnn.
telsoa014fcda012018-03-09 14:13:49 +00001842 if (layerParam.bottom_size() != 1)
1843 {
telsoa01c577f2c2018-08-31 09:22:23 +01001844 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001845 fmt::format("Split layer '{}' should have exactly 1 bottom. "
1846 "#bottoms={} {}",
1847 layerParam.name(),
1848 layerParam.bottom_size(),
1849 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001850 }
1851 armnn::IOutputSlot& outputSlot = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0));
1852 for (int i = 0; i < layerParam.top_size(); i++)
1853 {
1854 SetArmnnOutputSlotForCaffeTop(layerParam.top(i), outputSlot);
1855 }
1856}
1857
telsoa01c577f2c2018-08-31 09:22:23 +01001858void CaffeParserBase::ParseDropoutLayer(const caffe::LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +00001859{
telsoa01c577f2c2018-08-31 09:22:23 +01001860 // Ignored for inference, so patch the single input to its single output.
telsoa014fcda012018-03-09 14:13:49 +00001861 if (layerParam.bottom_size() != 1 || layerParam.top_size() != 1)
1862 {
telsoa01c577f2c2018-08-31 09:22:23 +01001863 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001864 fmt::format("Dropout layer '{}' should have exactly 1 bottom and 1 top. "
1865 "#bottoms={} #tops={} {}",
1866 layerParam.name(),
1867 layerParam.bottom_size(),
1868 layerParam.top_size(),
1869 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001870 }
1871 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)));
1872}
1873
telsoa01c577f2c2018-08-31 09:22:23 +01001874void CaffeParserBase::TrackInputBinding(armnn::IConnectableLayer* layer,
telsoa014fcda012018-03-09 14:13:49 +00001875 armnn::LayerBindingId id,
1876 const armnn::TensorInfo& tensorInfo)
1877{
1878 return TrackBindingPoint(layer, id, tensorInfo, layer->GetName(), m_NetworkInputsBindingInfo);
1879}
1880
telsoa01c577f2c2018-08-31 09:22:23 +01001881void CaffeParserBase::TrackOutputBinding(armnn::IConnectableLayer* layer,
telsoa014fcda012018-03-09 14:13:49 +00001882 armnn::LayerBindingId id,
1883 const armnn::TensorInfo& tensorInfo)
1884{
1885 return TrackBindingPoint(layer, id, tensorInfo, layer->GetName(), m_NetworkOutputsBindingInfo);
1886}
1887
telsoa01c577f2c2018-08-31 09:22:23 +01001888void CaffeParserBase::TrackBindingPoint(armnn::IConnectableLayer* layer,
telsoa014fcda012018-03-09 14:13:49 +00001889 armnn::LayerBindingId id,
1890 const armnn::TensorInfo& tensorInfo,
1891 const char* bindingPointDesc,
1892 std::unordered_map<std::string, BindingPointInfo>& nameToBindingInfo)
1893{
1894 const std::string layerName = layer->GetName();
1895 auto it = nameToBindingInfo.find(layerName);
1896 if (it == nameToBindingInfo.end())
1897 {
1898 nameToBindingInfo[layerName] = std::make_pair(id, tensorInfo);
1899 }
1900 else
1901 {
telsoa01c577f2c2018-08-31 09:22:23 +01001902 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001903 fmt::format("Id {} used by more than one {} layer {}",
1904 id,
1905 bindingPointDesc,
1906 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001907 }
1908}
1909
telsoa01c577f2c2018-08-31 09:22:23 +01001910armnn::IOutputSlot& CaffeParserBase::GetArmnnOutputSlotForCaffeTop(const std::string& caffeTopName) const
telsoa014fcda012018-03-09 14:13:49 +00001911{
1912 auto it = m_ArmnnOutputSlotForCaffeTop.find(caffeTopName);
1913 if (it != m_ArmnnOutputSlotForCaffeTop.end())
1914 {
1915 return *it->second;
1916 }
1917 else
1918 {
telsoa01c577f2c2018-08-31 09:22:23 +01001919 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001920 fmt::format("Could not find armnn output slot for Caffe top '{}' {}",
1921 caffeTopName,
1922 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001923 }
1924}
1925
telsoa01c577f2c2018-08-31 09:22:23 +01001926void CaffeParserBase::SetArmnnOutputSlotForCaffeTop(
1927 const std::string& caffeTopName, armnn::IOutputSlot& armnnOutputSlot)
telsoa014fcda012018-03-09 14:13:49 +00001928{
1929 auto it = m_ArmnnOutputSlotForCaffeTop.find(caffeTopName);
1930 if (it == m_ArmnnOutputSlotForCaffeTop.end())
1931 {
1932 m_ArmnnOutputSlotForCaffeTop[caffeTopName] = &armnnOutputSlot;
1933 }
1934 else
1935 {
telsoa01c577f2c2018-08-31 09:22:23 +01001936 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001937 fmt::format("Attempting to add duplicate entry for Caffe top '{}' {}",
1938 caffeTopName,
1939 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001940 }
1941}
1942
telsoa01c577f2c2018-08-31 09:22:23 +01001943// Note: can move to CaffeParser when/if we optimise the text/string format
1944// to load on a layer by layer basis
1945void CaffeParserBase::ResolveInPlaceLayers(caffe::NetParameter& netParameter)
telsoa014fcda012018-03-09 14:13:49 +00001946{
telsoa01c577f2c2018-08-31 09:22:23 +01001947 // Finds layers with the same top.
telsoa014fcda012018-03-09 14:13:49 +00001948 std::map<std::string, std::vector<caffe::LayerParameter*>> layersByTop;
1949 for (int layerIdx = 0; layerIdx < netParameter.layer_size(); ++layerIdx)
1950 {
1951 caffe::LayerParameter& layer = *netParameter.mutable_layer(layerIdx);
telsoa01c577f2c2018-08-31 09:22:23 +01001952 std::string name = layer.name();
telsoa014fcda012018-03-09 14:13:49 +00001953 for (int i = 0; i < layer.top_size(); ++i)
1954 {
1955 layersByTop[layer.top(i)].push_back(&layer);
1956 }
1957 }
1958
telsoa01c577f2c2018-08-31 09:22:23 +01001959 // For each set of layers with the same top, resolves them to a linear chain rather than in-place layers.
telsoa014fcda012018-03-09 14:13:49 +00001960 // Note that for 'regular' layers, there will be a single layer in each group and so this will be a no-op.
1961 for (auto layersWithSameTopIt : layersByTop)
1962 {
1963 const std::string& top = layersWithSameTopIt.first;
1964 const std::vector<caffe::LayerParameter*>& layersWithSameTop = layersWithSameTopIt.second;
1965
telsoa01c577f2c2018-08-31 09:22:23 +01001966 // Chains the layers together in the order that they are listed in the prototxt (hopefully this is correct).
telsoa014fcda012018-03-09 14:13:49 +00001967 // Note that the last layer will not have its top modified so that other layers will continue to reference it.
1968 for (unsigned int layerIdx = 0; layerIdx < layersWithSameTop.size() - 1; ++layerIdx)
1969 {
1970 caffe::LayerParameter& layer1 = *layersWithSameTop[layerIdx];
1971 caffe::LayerParameter& layer2 = *layersWithSameTop[layerIdx+1];
1972 if (layer1.top_size() != 1)
1973 {
telsoa01c577f2c2018-08-31 09:22:23 +01001974 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001975 fmt::format("Node '{}' is an in-place layer but doesn't have exactly one "
1976 "top. It has {} instead. {}",
1977 layer1.name(),
1978 layer1.top_size(),
1979 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001980 }
1981 std::string newTop = layer1.name() + "_top";
1982 layer1.set_top(0, newTop);
1983 if (layer2.bottom_size() != 1 || layer2.bottom(0) != top)
1984 {
telsoa01c577f2c2018-08-31 09:22:23 +01001985 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001986 fmt::format("Node '{}' is an in-place layer but "
1987 "doesn't have exactly one bottom, or it doesn't match its top. "
1988 "#bottoms={}, first bottom is {}, top is {} {}",
1989 layer2.name(),
1990 layer2.bottom(0),
1991 top,
1992 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001993 }
1994 layer2.set_bottom(0, newTop);
1995 }
1996 }
1997}
1998
telsoa01c577f2c2018-08-31 09:22:23 +01001999// Note: can move to CaffeParser when/if we optimise the text/string format
2000// to load on a layer by layer basis
2001void CaffeParserBase::LoadNetParam(NetParameter& netParameter)
telsoa014fcda012018-03-09 14:13:49 +00002002{
telsoa01c577f2c2018-08-31 09:22:23 +01002003 // Caffe models sometimes have an implicit input layer.
2004 // In that case, add an explicit one.
telsoa014fcda012018-03-09 14:13:49 +00002005 if (netParameter.input_size() > 0)
2006 {
2007 LayerParameter* newLayer = netParameter.add_layer();
2008
2009 newLayer->set_type("Input");
2010 newLayer->set_name(netParameter.input(0));
2011 newLayer->add_top(netParameter.input(0));
2012
2013 InputParameter* inputParam = newLayer->mutable_input_param();
2014 BlobShape* shape = inputParam->add_shape();
2015
2016 int dim_size = netParameter.input_dim_size();
2017 for (int i = 0; i < dim_size; ++i)
2018 {
2019 shape->add_dim(netParameter.input_dim(i));
2020 }
2021 }
2022
telsoa01c577f2c2018-08-31 09:22:23 +01002023 // Replaces in-place layers with regular ones to make the rest of the parsing easier.
telsoa014fcda012018-03-09 14:13:49 +00002024 ResolveInPlaceLayers(netParameter);
2025
telsoa01c577f2c2018-08-31 09:22:23 +01002026 // Creates a lookup of Caffe layers by name.
telsoa014fcda012018-03-09 14:13:49 +00002027 for (int i = 0; i < netParameter.layer_size(); ++i)
2028 {
2029 const caffe::LayerParameter& layer = netParameter.layer(i);
2030 for (int i = 0; i < layer.top_size(); ++i)
2031 {
2032 m_CaffeLayersByTopName[layer.top(i)] = &layer;
2033 }
2034 }
2035
telsoa01c577f2c2018-08-31 09:22:23 +01002036 // Finds the output layers the user requested.
telsoa014fcda012018-03-09 14:13:49 +00002037 std::vector<const caffe::LayerParameter*> targetLayers;
2038 for (const std::string& requestedOutputName : m_RequestedOutputs)
2039 {
2040 auto nodeIt = m_CaffeLayersByTopName.find(requestedOutputName);
2041 if (nodeIt == m_CaffeLayersByTopName.end())
2042 {
telsoa01c577f2c2018-08-31 09:22:23 +01002043 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002044 fmt::format("Couldn't find requested output layer '{}' in graph {}",
2045 requestedOutputName,
2046 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00002047 }
2048 targetLayers.push_back(nodeIt->second);
2049 }
2050
telsoa01c577f2c2018-08-31 09:22:23 +01002051 // Sorts them into a linear ordering such that all inputs of a node are before the node itself.
telsoa014fcda012018-03-09 14:13:49 +00002052 std::vector<const caffe::LayerParameter*> sortedNodes;
2053 if (!armnnUtils::GraphTopologicalSort<const caffe::LayerParameter*>(
2054 targetLayers,
2055 [this](const caffe::LayerParameter* node)
2056 {
2057 return GetInputs(*node);
2058 },
2059 sortedNodes))
2060 {
telsoa01c577f2c2018-08-31 09:22:23 +01002061 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002062 fmt::format("Cycle detected in graph. #nodes: {} {}",
2063 sortedNodes.size(),
2064 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00002065 }
2066
telsoa01c577f2c2018-08-31 09:22:23 +01002067 // Parses each node in order, knowing that all inputs of a node will be processed before the node itself.
telsoa014fcda012018-03-09 14:13:49 +00002068 for (const caffe::LayerParameter* current : sortedNodes)
2069 {
2070 auto it = ms_CaffeLayerNameToParsingFunctions.find(current->type());
2071 if (it == ms_CaffeLayerNameToParsingFunctions.end())
2072 {
telsoa01c577f2c2018-08-31 09:22:23 +01002073 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002074 fmt::format("Unsupported layer type: '{}' for layer {} {}",
2075 current->type(),
2076 current->name(),
2077 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00002078 }
2079 auto func = it->second;
2080 (this->*func)(*current);
2081 }
2082
telsoa01c577f2c2018-08-31 09:22:23 +01002083 // Adds ArmNN output layers connected to each requested output.
telsoa014fcda012018-03-09 14:13:49 +00002084 for (const std::string& requestedOutput : m_RequestedOutputs)
2085 {
2086 armnn::IOutputSlot& outputSlot = GetArmnnOutputSlotForCaffeTop(requestedOutput);
2087
Matthew Sloyan589e3e82020-09-11 16:17:48 +01002088 const armnn::LayerBindingId outputId = armnn::numeric_cast<armnn::LayerBindingId>(
telsoa014fcda012018-03-09 14:13:49 +00002089 m_NetworkOutputsBindingInfo.size());
2090 armnn::IConnectableLayer* const outputLayer = m_Network->AddOutputLayer(outputId, requestedOutput.c_str());
2091 outputSlot.Connect(outputLayer->GetInputSlot(0));
2092
2093 TrackOutputBinding(outputLayer, outputId, outputLayer->GetInputSlot(0).GetConnection()->GetTensorInfo());
2094 }
2095}
2096
telsoa01c577f2c2018-08-31 09:22:23 +01002097INetworkPtr CaffeParserBase::CreateNetworkFromTextFile(const char* graphFile,
telsoa014fcda012018-03-09 14:13:49 +00002098 const std::map<std::string, armnn::TensorShape>& inputShapes,
2099 const std::vector<std::string>& requestedOutputs)
2100{
2101 FILE* fd = fopen(graphFile, "r");
2102
2103 if (fd == nullptr)
2104 {
telsoa01c577f2c2018-08-31 09:22:23 +01002105 throw FileNotFoundException(
James Ward58dec6b2020-09-11 17:32:44 +01002106 fmt::format("Failed to open graph file: {} {}",
2107 graphFile,
2108 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00002109 }
2110
telsoa01c577f2c2018-08-31 09:22:23 +01002111 // Parses the file into a message.
telsoa014fcda012018-03-09 14:13:49 +00002112 NetParameter netParam;
2113 auto input = new google::protobuf::io::FileInputStream(fileno(fd));
2114 bool success = google::protobuf::TextFormat::Parse(input, &netParam);
2115 delete input;
2116 fclose(fd);
2117
2118 if (!success)
2119 {
telsoa01c577f2c2018-08-31 09:22:23 +01002120 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002121 fmt::format("Failed to parse graph file: {} {}",
2122 graphFile,
2123 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00002124 }
2125
2126 return CreateNetworkFromNetParameter(netParam, inputShapes, requestedOutputs);
2127}
2128
telsoa01c577f2c2018-08-31 09:22:23 +01002129INetworkPtr CaffeParserBase::CreateNetworkFromString(const char* protoText,
telsoa014fcda012018-03-09 14:13:49 +00002130 const std::map<std::string, armnn::TensorShape>& inputShapes,
2131 const std::vector<std::string>& requestedOutputs)
2132{
telsoa01c577f2c2018-08-31 09:22:23 +01002133 // Parses the string into a message.
telsoa014fcda012018-03-09 14:13:49 +00002134 NetParameter netParam;
2135 bool success = google::protobuf::TextFormat::ParseFromString(protoText, &netParam);
2136
2137 if (!success)
2138 {
telsoa01c577f2c2018-08-31 09:22:23 +01002139 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002140 fmt::format("Failed to parse graph string {}",
2141 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00002142 }
2143
2144 return CreateNetworkFromNetParameter(netParam, inputShapes, requestedOutputs);
2145}
2146
2147INetworkPtr CaffeParser::CreateNetworkFromBinaryFile(const char* graphFile,
2148 const std::map<std::string, armnn::TensorShape>& inputShapes,
2149 const std::vector<std::string>& requestedOutputs)
2150{
2151 FILE* fd = fopen(graphFile, "rb");
2152
2153 if (fd == nullptr)
2154 {
telsoa01c577f2c2018-08-31 09:22:23 +01002155 throw FileNotFoundException(
James Ward58dec6b2020-09-11 17:32:44 +01002156 fmt::format("Failed to open graph file at: {} {}",
2157 graphFile,
2158 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00002159 }
2160
telsoa01c577f2c2018-08-31 09:22:23 +01002161 // Parses the file into a message.
telsoa014fcda012018-03-09 14:13:49 +00002162 NetParameter netParam;
2163
2164 FileInputStream inStream(fileno(fd));
2165 CodedInputStream codedStream(&inStream);
Nikhil Raje5181532020-10-09 14:52:25 +01002166 codedStream.SetTotalBytesLimit(INT_MAX);
telsoa014fcda012018-03-09 14:13:49 +00002167 bool success = netParam.ParseFromCodedStream(&codedStream);
2168 fclose(fd);
2169
2170 if (!success)
2171 {
telsoa01c577f2c2018-08-31 09:22:23 +01002172 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002173 fmt::format("Failed to parse protobuf file: {} {}",
2174 graphFile,
2175 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00002176 }
2177
2178 return CreateNetworkFromNetParameter(netParam, inputShapes, requestedOutputs);
2179}
2180
telsoa01c577f2c2018-08-31 09:22:23 +01002181// Note: can move to CaffeParser when/if we optimise the text/string format
2182// to load on a layer by layer basis
2183INetworkPtr CaffeParserBase::CreateNetworkFromNetParameter(NetParameter& netParam,
telsoa014fcda012018-03-09 14:13:49 +00002184 const std::map<std::string, armnn::TensorShape>& inputShapes,
2185 const std::vector<std::string>& requestedOutputs)
2186{
2187 m_NetworkInputsBindingInfo.clear();
2188 m_NetworkOutputsBindingInfo.clear();
2189
2190 m_Network = INetwork::Create();
2191
2192 m_InputShapes = inputShapes;
2193 if (requestedOutputs.size() == 0)
2194 {
2195 throw ParseException("requestedOutputs must have at least one entry");
2196 }
2197 m_RequestedOutputs = requestedOutputs;
2198
2199 try
2200 {
2201 LoadNetParam(netParam);
2202 }
2203 catch (const ParseException& e)
2204 {
2205 Cleanup();
2206 throw e;
2207 }
2208
2209 Cleanup();
2210
2211 return move(m_Network);
2212}
2213
telsoa01c577f2c2018-08-31 09:22:23 +01002214void CaffeParserBase::Cleanup() {
telsoa014fcda012018-03-09 14:13:49 +00002215 // cleanup, in case we reuse this parser
telsoa014fcda012018-03-09 14:13:49 +00002216 m_InputShapes.clear();
2217 m_RequestedOutputs.clear();
2218 m_ArmnnOutputSlotForCaffeTop.clear();
telsoa01c577f2c2018-08-31 09:22:23 +01002219 // NOTE: when we get the text/string format
2220 // optimised for memory then this data structure can
2221 // also move to the CaffeParser class
2222 m_CaffeLayersByTopName.clear();
telsoa014fcda012018-03-09 14:13:49 +00002223}
2224
2225}