blob: 463f3eb2a5636e353306c9133c149e087bf42520 [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
Matthew Sloyanac001ee2021-02-03 10:43:04 +00008#include "armnnCaffeParser/Version.hpp"
9
telsoa014fcda012018-03-09 14:13:49 +000010#include "armnn/Descriptors.hpp"
11#include "armnn/INetwork.hpp"
12#include "armnn/Utils.hpp"
13#include "armnn/Exceptions.hpp"
14
15#include "GraphTopologicalSort.hpp"
telsoa01c577f2c2018-08-31 09:22:23 +010016#include "VerificationHelpers.hpp"
telsoa014fcda012018-03-09 14:13:49 +000017
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +010018#include <armnn/utility/Assert.hpp>
Matthew Sloyan589e3e82020-09-11 16:17:48 +010019#include <armnn/utility/NumericCast.hpp>
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +010020
James Ward58dec6b2020-09-11 17:32:44 +010021#include <fmt/format.h>
telsoa014fcda012018-03-09 14:13:49 +000022
23// Caffe
24#include "caffe/proto/caffe.pb.h"
25
26// ProtoBuf
27#include <google/protobuf/io/coded_stream.h>
28#include <google/protobuf/io/zero_copy_stream.h>
29#include <google/protobuf/io/zero_copy_stream_impl.h>
30#include <google/protobuf/text_format.h>
31#include <google/protobuf/stubs/common.h>
32#include <google/protobuf/stubs/once.h>
33#include <google/protobuf/io/coded_stream.h>
telsoa014fcda012018-03-09 14:13:49 +000034#include <google/protobuf/descriptor.h>
35#include <google/protobuf/generated_message_reflection.h>
36#include <google/protobuf/reflection_ops.h>
37#include <google/protobuf/wire_format.h>
38
39#include <cmath>
Matthew Sloyanac001ee2021-02-03 10:43:04 +000040#include <iostream>
telsoa014fcda012018-03-09 14:13:49 +000041#include <sstream>
42#include <queue>
43#include <fcntl.h>
44
45/// Caffe networks are loaded from protobuf files (binary or text) using the protobuf library and the generated
46/// code from caffe.pb.h. This gives us a caffe::NetParameter which is an in-memory version of the file.
47/// This contains a flat list of Caffe 'layers' (e.g. convolution, pooling etc.).
48/// Each layer has inputs (called "bottoms") and outputs (called "tops"). Data flows from bottom to top.
49/// The bottoms of a layer refer to the tops of other layers, not their names.
telsoa01c577f2c2018-08-31 09:22:23 +010050/// The names of layers seem to be arbitrary (you could rename a layer and the network wouldn't
51/// need any other changes).
telsoa014fcda012018-03-09 14:13:49 +000052///
53/// Some layers (e.g. Relu) can be configured so that their top and bottom are both the same. This is called an
54/// "in-place" layer and is a Caffe runtime feature used to reduce memory usage by modifying tensors in-place.
55/// This isn't relevant to the parser and so we preprocess these layers to convert them to regular layers, to result
56/// in a consistent graph structure.
57
58namespace armnnCaffeParser
59{
60
61using namespace armnn;
62using namespace caffe;
63using namespace std;
64using namespace google::protobuf::io;
65
Kevin Mayef33cb12021-01-29 14:24:57 +000066ICaffeParser::ICaffeParser() : pCaffeParserImpl(new RecordByRecordCaffeParser()) {}
67
68ICaffeParser::~ICaffeParser() = default;
69
70ICaffeParser* ICaffeParser::CreateRaw()
71{
72 return new ICaffeParser();
73}
74
75ICaffeParserPtr ICaffeParser::Create()
76{
77 return ICaffeParserPtr(CreateRaw(), &ICaffeParser::Destroy);
78}
79
80void ICaffeParser::Destroy(ICaffeParser* parser)
81{
82 delete parser;
83}
84
85armnn::INetworkPtr ICaffeParser::CreateNetworkFromTextFile(
86 const char* graphFile,
87 const std::map<std::string, armnn::TensorShape>& inputShapes,
88 const std::vector<std::string>& requestedOutputs)
89{
90 return pCaffeParserImpl->CreateNetworkFromTextFile(graphFile, inputShapes, requestedOutputs);
91}
92
93armnn::INetworkPtr ICaffeParser::CreateNetworkFromBinaryFile(
94 const char* graphFile,
95 const std::map<std::string, armnn::TensorShape>& inputShapes,
96 const std::vector<std::string>& requestedOutputs)
97{
98 return pCaffeParserImpl->CreateNetworkFromBinaryFile(graphFile, inputShapes,requestedOutputs);
99}
100
101armnn::INetworkPtr ICaffeParser::CreateNetworkFromString(
102 const char* protoText,
103 const std::map<std::string, armnn::TensorShape>& inputShapes,
104 const std::vector<std::string>& requestedOutputs)
105{
106 return pCaffeParserImpl->CreateNetworkFromString(protoText, inputShapes, requestedOutputs);
107}
108
109BindingPointInfo ICaffeParser::GetNetworkInputBindingInfo(const std::string& name) const
110{
111 return pCaffeParserImpl->GetNetworkInputBindingInfo(name);
112}
113
114BindingPointInfo ICaffeParser::GetNetworkOutputBindingInfo(const std::string& name) const
115{
116 return pCaffeParserImpl->GetNetworkOutputBindingInfo(name);
117}
118
telsoa01c577f2c2018-08-31 09:22:23 +0100119namespace
telsoa014fcda012018-03-09 14:13:49 +0000120{
121
telsoa01c577f2c2018-08-31 09:22:23 +0100122const float* GetArrayPtrFromBlob(const LayerParameter& layerParam, unsigned int blobIndex)
telsoa014fcda012018-03-09 14:13:49 +0000123{
telsoa01c577f2c2018-08-31 09:22:23 +0100124 auto nBlobs = layerParam.blobs_size();
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100125 if (blobIndex >= armnn::numeric_cast<unsigned int>(nBlobs))
telsoa014fcda012018-03-09 14:13:49 +0000126 {
telsoa01c577f2c2018-08-31 09:22:23 +0100127 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100128 fmt::format("Expected data blob at index {} in layer {} not found. nBlobs={}. {}",
129 blobIndex,
130 layerParam.name(),
131 nBlobs,
132 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +0000133 }
134
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100135 const BlobProto& blob = layerParam.blobs(armnn::numeric_cast<int>(blobIndex));
telsoa014fcda012018-03-09 14:13:49 +0000136
telsoa01c577f2c2018-08-31 09:22:23 +0100137 const float* arrayPtr = blob.data().data();
138 return arrayPtr;
139}
140
141void GetDataFromBlob(const LayerParameter& layerParam, vector<float>& outData, unsigned int blobIndex)
142{
143 auto nBlobs = layerParam.blobs_size();
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100144 if (blobIndex >= armnn::numeric_cast<unsigned int>(nBlobs))
telsoa014fcda012018-03-09 14:13:49 +0000145 {
telsoa01c577f2c2018-08-31 09:22:23 +0100146 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100147 fmt::format("Expected data blob at index {} in layer {} not found. {}",
148 blobIndex,
149 layerParam.name(),
150 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +0000151 }
152
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100153 const BlobProto& blob = layerParam.blobs(armnn::numeric_cast<int>(blobIndex));
telsoa01c577f2c2018-08-31 09:22:23 +0100154
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100155 size_t blobSize = armnn::numeric_cast<size_t>(blob.data_size());
telsoa01c577f2c2018-08-31 09:22:23 +0100156 if (blobSize != outData.size())
telsoa014fcda012018-03-09 14:13:49 +0000157 {
telsoa01c577f2c2018-08-31 09:22:23 +0100158 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100159 fmt::format("Data blob at index {} in layer {} has an unexpected size. "
160 "Expected {} elements but got {} elements. {}",
161 blobIndex,
162 layerParam.name(),
163 outData.size(),
164 blobSize,
165 CHECK_LOCATION().AsString()));
telsoa01c577f2c2018-08-31 09:22:23 +0100166 }
167
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100168 int outSizeInt = armnn::numeric_cast<int>(outData.size());
telsoa01c577f2c2018-08-31 09:22:23 +0100169 for (int i = 0; i < outSizeInt; ++i)
170 {
171 outData[static_cast<size_t>(i)] = blob.data(i);
telsoa014fcda012018-03-09 14:13:49 +0000172 }
173}
174
telsoa014fcda012018-03-09 14:13:49 +0000175template <typename T>
176size_t SizeOfVectorData(const vector<T>& vec)
177{
178 return vec.size() * sizeof(T);
179}
180
181void ValidateNumInputsOutputs(const caffe::LayerParameter& layerParameter,
182 unsigned int numInputs,
183 unsigned int numOutputs)
184{
185 int numInputsActual = layerParameter.bottom_size();
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100186 if (numInputs != armnn::numeric_cast<unsigned int>(numInputsActual))
telsoa014fcda012018-03-09 14:13:49 +0000187 {
telsoa01c577f2c2018-08-31 09:22:23 +0100188 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100189 fmt::format("Invalid number of inputs requested {} for layer {} "
190 "while only {} present. {}",
191 numInputs,
192 layerParameter.name(),
193 numInputsActual,
194 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +0000195 }
196
197 int numOutputsActual = layerParameter.top_size();
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100198 if (numOutputs != armnn::numeric_cast<unsigned int>(numOutputsActual))
telsoa014fcda012018-03-09 14:13:49 +0000199 {
telsoa01c577f2c2018-08-31 09:22:23 +0100200 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100201 fmt::format("Invalid number of outputs requested {} for layer {} "
202 "while only {} present. {}",
203 numOutputs,
204 layerParameter.name(),
205 numOutputsActual,
206 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +0000207 }
208}
209
telsoa01c577f2c2018-08-31 09:22:23 +0100210template <typename ParamType, typename ExtractOptional, typename ExtractFallback, typename ValueType>
211ValueType GetOptionalWithFallback(const ParamType& param,
212 ExtractOptional extractOptional,
213 ExtractFallback extractFallback,
214 ValueType defaultValue)
215{
216 auto optValue = extractOptional(param, defaultValue);
217 if (optValue.first)
218 {
219 return optValue.second;
220 }
221 auto fallbackValue = extractFallback(param, defaultValue);
222 return fallbackValue.second;
223}
224
225#define GET_OPTIONAL_WITH_VECTOR_FALLBACK(PARAM, \
226 PARAM_TYPE, \
227 OPTIONAL_VALUE, \
228 FALLBACK_VECTOR, \
229 VALUE_TYPE, \
230 DEFAULT_VALUE) \
231 GetOptionalWithFallback( \
232 PARAM, \
233 [](const PARAM_TYPE & param, VALUE_TYPE defaultValue) \
234 { \
235 if (param.has_##OPTIONAL_VALUE ()) \
236 { \
237 return std::make_pair(true, param.OPTIONAL_VALUE ()); \
238 } \
239 else \
240 { \
241 return std::make_pair(false, defaultValue); \
242 } \
243 }, \
244 [](const PARAM_TYPE & param, VALUE_TYPE defaultValue) \
245 { \
246 if (param.FALLBACK_VECTOR##_size() > 0) \
247 { \
248 return std::make_pair(true, (param.FALLBACK_VECTOR ()).Get(0)); \
249 } \
250 else \
251 { \
252 return std::make_pair(false, defaultValue); \
253 } \
254 }, \
255 DEFAULT_VALUE)
256
257#define GET_OPTIONAL_WITH_FALLBACK(PARAM, \
258 PARAM_TYPE, \
259 OPTIONAL_VALUE, \
260 FALLBACK_VALUE, \
261 VALUE_TYPE, \
262 DEFAULT_VALUE) \
263 GetOptionalWithFallback( \
264 PARAM, \
265 [](const PARAM_TYPE & param, VALUE_TYPE defaultValue) \
266 { \
267 if (param.has_##OPTIONAL_VALUE ()) \
268 { \
269 return std::make_pair(true, param.OPTIONAL_VALUE ()); \
270 } \
271 else \
272 { \
273 return std::make_pair(false, defaultValue); \
274 } \
275 }, \
276 [](const PARAM_TYPE & param, VALUE_TYPE defaultValue) \
277 { \
278 if (param.has_##FALLBACK_VALUE ()) \
279 { \
280 return std::make_pair(true, param.FALLBACK_VALUE ()); \
281 } \
282 else \
283 { \
284 return std::make_pair(false, defaultValue); \
285 } \
286 }, \
287 DEFAULT_VALUE)
288
telsoa01c577f2c2018-08-31 09:22:23 +0100289} // namespace <anonymous>
290
Kevin Mayef33cb12021-01-29 14:24:57 +0000291const std::map<std::string, ICaffeParser::CaffeParserImpl::OperationParsingFunction>
292 ICaffeParser::CaffeParserImpl::ms_CaffeLayerNameToParsingFunctions = {
293 { "Input", &CaffeParserImpl::ParseInputLayer },
294 { "Convolution", &CaffeParserImpl::ParseConvLayer },
295 { "Deconvolution",&CaffeParserImpl::ParseDeconvLayer },
296 { "Pooling", &CaffeParserImpl::ParsePoolingLayer },
297 { "ReLU", &CaffeParserImpl::ParseReluLayer },
298 { "LRN", &CaffeParserImpl::ParseLRNLayer },
299 { "InnerProduct", &CaffeParserImpl::ParseInnerProductLayer },
300 { "Softmax", &CaffeParserImpl::ParseSoftmaxLayer },
301 { "Eltwise", &CaffeParserImpl::ParseEltwiseLayer },
302 { "Concat", &CaffeParserImpl::ParseConcatLayer },
303 { "BatchNorm", &CaffeParserImpl::ParseBatchNormLayer },
304 { "Scale", &CaffeParserImpl::ParseScaleLayer },
305 { "Split", &CaffeParserImpl::ParseSplitLayer },
306 { "Dropout", &CaffeParserImpl::ParseDropoutLayer},
307 { "ArgMax", &CaffeParserImpl::ParseArgmaxLayer},
telsoa01c577f2c2018-08-31 09:22:23 +0100308};
309
Kevin Mayef33cb12021-01-29 14:24:57 +0000310ICaffeParser::CaffeParserImpl::CaffeParserImpl()
telsoa01c577f2c2018-08-31 09:22:23 +0100311 : m_Network(nullptr, nullptr)
312{
313
314}
315
316CaffeParser::CaffeParser()
Kevin Mayef33cb12021-01-29 14:24:57 +0000317: CaffeParserImpl()
telsoa01c577f2c2018-08-31 09:22:23 +0100318{
319
320}
321
Kevin Mayef33cb12021-01-29 14:24:57 +0000322BindingPointInfo ICaffeParser::CaffeParserImpl::GetNetworkInputBindingInfo(const std::string& name) const
telsoa014fcda012018-03-09 14:13:49 +0000323{
324 return GetBindingInfo(name, "input", m_NetworkInputsBindingInfo);
325}
326
Kevin Mayef33cb12021-01-29 14:24:57 +0000327BindingPointInfo ICaffeParser::CaffeParserImpl::GetNetworkOutputBindingInfo(const std::string& name) const
telsoa014fcda012018-03-09 14:13:49 +0000328{
329 return GetBindingInfo(name, "output", m_NetworkOutputsBindingInfo);
330}
331
Kevin Mayef33cb12021-01-29 14:24:57 +0000332std::pair<armnn::LayerBindingId, armnn::TensorInfo> ICaffeParser::CaffeParserImpl::GetBindingInfo(
333 const std::string& layerName,
telsoa014fcda012018-03-09 14:13:49 +0000334 const char* bindingPointDesc,
335 const std::unordered_map<std::string, BindingPointInfo>& nameToBindingInfo)
336{
337 auto it = nameToBindingInfo.find(layerName);
338 if (it == nameToBindingInfo.end())
339 {
telsoa01c577f2c2018-08-31 09:22:23 +0100340 throw InvalidArgumentException(
James Ward58dec6b2020-09-11 17:32:44 +0100341 fmt::format("Unknown binding {} for layer '{}'. {}",
342 bindingPointDesc,
343 layerName,
344 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +0000345 }
346 return it->second;
347}
348
Kevin Mayef33cb12021-01-29 14:24:57 +0000349TensorInfo ICaffeParser::CaffeParserImpl::BlobShapeToTensorInfo(const caffe::BlobShape& blobShape) const
telsoa014fcda012018-03-09 14:13:49 +0000350{
351 std::vector<unsigned int> shape;
352 for (int j = 0; j < blobShape.dim_size(); ++j)
353 {
354 shape.push_back(static_cast<unsigned int>(blobShape.dim(j)));
355 }
356
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100357 return TensorInfo(armnn::numeric_cast<unsigned int>(shape.size()), shape.data(), DataType::Float32);
telsoa014fcda012018-03-09 14:13:49 +0000358}
359
360BlobShape TensorDescToBlobShape(const TensorInfo& desc)
361{
362 BlobShape ret;
363 for (unsigned int i = 0; i < desc.GetNumDimensions(); ++i)
364 {
365 ret.add_dim(i);
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100366 ret.set_dim(armnn::numeric_cast<int>(i), desc.GetShape()[i]);
telsoa014fcda012018-03-09 14:13:49 +0000367 }
368
369 return ret;
370}
371
telsoa01c577f2c2018-08-31 09:22:23 +0100372// Note: can move to CaffeParser when/if we optimise the text/string format
373// to load on a layer by layer basis
Kevin Mayef33cb12021-01-29 14:24:57 +0000374vector<const LayerParameter*> ICaffeParser::CaffeParserImpl::GetInputs(const LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +0000375{
376 std::vector<const caffe::LayerParameter*> ret;
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100377 ret.reserve(armnn::numeric_cast<size_t>(layerParam.bottom_size()));
telsoa014fcda012018-03-09 14:13:49 +0000378 for (int j = 0; j < layerParam.bottom_size(); ++j)
379 {
380 std::string inputName = layerParam.bottom(j);
381 auto inputIt = m_CaffeLayersByTopName.find(inputName);
382 if (inputIt == m_CaffeLayersByTopName.end())
383 {
384 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100385 fmt::format("Can't find Caffe layer with top called '{}', "
386 "which is listed as an input of '{}'. {}",
387 inputName,
388 layerParam.name(),
389 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +0000390 }
391 ret.push_back(inputIt->second);
392 }
393
394 return ret;
395}
396
Kevin Mayef33cb12021-01-29 14:24:57 +0000397void ICaffeParser::CaffeParserImpl::ParseInputLayer(const LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +0000398{
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100399 ARMNN_ASSERT(layerParam.type() == "Input");
telsoa014fcda012018-03-09 14:13:49 +0000400 ValidateNumInputsOutputs(layerParam, 0, 1);
401
402 const InputParameter& param = layerParam.input_param();
403
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100404 const armnn::LayerBindingId inputId = armnn::numeric_cast<armnn::LayerBindingId>(
telsoa01c577f2c2018-08-31 09:22:23 +0100405 m_NetworkInputsBindingInfo.size());
telsoa014fcda012018-03-09 14:13:49 +0000406 armnn::IConnectableLayer* const inputLayer = m_Network->AddInputLayer(inputId, layerParam.name().c_str());
407
telsoa01c577f2c2018-08-31 09:22:23 +0100408 // Decides the tensor info for this input. This can be specified in the Caffe network but can also
telsoa014fcda012018-03-09 14:13:49 +0000409 // be overriden by user input (m_inputShapes).
410 armnn::TensorInfo inputTensorInfo;
411
412 const BlobShape* originalShape = param.shape_size() > 0 && param.shape(0).dim_size() > 0 ?
413 &param.shape(0) : nullptr;
414 if (originalShape)
415 {
416 inputTensorInfo = BlobShapeToTensorInfo(*originalShape);
417 }
418
419 auto overrideIt = m_InputShapes.find(layerParam.name());
420 if (overrideIt != m_InputShapes.end())
421 {
422 const TensorShape& overrideShape = overrideIt->second;
423 if (originalShape &&
424 ( originalShape->dim(1) != overrideShape[1]
425 || originalShape->dim(2) != overrideShape[2]
426 || originalShape->dim(3) != overrideShape[3]))
427 {
telsoa01c577f2c2018-08-31 09:22:23 +0100428 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100429 fmt::format("Parsed input shape for '{}' is incompatible with the override provided. {}",
430 layerParam.name(),
431 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +0000432 }
433 inputTensorInfo.SetShape(overrideShape);
434 }
435 else if (!originalShape)
436 {
telsoa01c577f2c2018-08-31 09:22:23 +0100437 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100438 fmt::format("No input descriptor given for '{}' and no input shape found in caffe model. {}",
439 layerParam.name(),
440 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +0000441 }
telsoa014fcda012018-03-09 14:13:49 +0000442 TrackInputBinding(inputLayer, inputId, inputTensorInfo);
443 inputLayer->GetOutputSlot(0).SetTensorInfo(inputTensorInfo);
444 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), inputLayer->GetOutputSlot(0));
445}
446
Kevin Mayef33cb12021-01-29 14:24:57 +0000447void ICaffeParser::CaffeParserImpl::AddConvLayerWithSplits(const caffe::LayerParameter& layerParam,
448 const armnn::Convolution2dDescriptor& desc,
449 unsigned int kernelW,
450 unsigned int kernelH)
telsoa014fcda012018-03-09 14:13:49 +0000451{
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100452 ARMNN_ASSERT(layerParam.type() == "Convolution");
telsoa014fcda012018-03-09 14:13:49 +0000453 ValidateNumInputsOutputs(layerParam, 1, 1);
454
telsoa01c577f2c2018-08-31 09:22:23 +0100455 ConvolutionParameter convParam = layerParam.convolution_param();
telsoa014fcda012018-03-09 14:13:49 +0000456 BlobShape inputShape = TensorDescToBlobShape(GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo());
telsoa01c577f2c2018-08-31 09:22:23 +0100457 const unsigned int numGroups = convParam.has_group() ? convParam.group() : 1;
telsoa014fcda012018-03-09 14:13:49 +0000458
telsoa01c577f2c2018-08-31 09:22:23 +0100459 // asusme these were already verified by the caller ParseConvLayer() function
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100460 ARMNN_ASSERT(numGroups < inputShape.dim(1));
461 ARMNN_ASSERT(numGroups > 1);
telsoa014fcda012018-03-09 14:13:49 +0000462
463 // Handle grouping
telsoa014fcda012018-03-09 14:13:49 +0000464 armnn::IOutputSlot& inputConnection = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0));
465
466 vector<string> convLayerNames(numGroups);
467 vector<armnn::IConnectableLayer*> convLayers(numGroups);
468 convLayerNames[0] = layerParam.name();
469
telsoa01c577f2c2018-08-31 09:22:23 +0100470 // This convolution is to be applied to chunks of the input data so add a splitter layer
471
472 // Redirect the convolution input to the splitter
473 unsigned int splitterDimSizes[4] = {static_cast<unsigned int>(inputShape.dim(0)),
474 static_cast<unsigned int>(inputShape.dim(1)),
475 static_cast<unsigned int>(inputShape.dim(2)),
476 static_cast<unsigned int>(inputShape.dim(3))};
477
478 // Split dimension 1 of the splitter output shape and conv input shapes
479 // according to the number of groups
480
481 splitterDimSizes[1] /= numGroups;
482 inputShape.set_dim(1, splitterDimSizes[1]);
483
484 // This is used to describe how the input is to be split
485 ViewsDescriptor splitterDesc(numGroups);
486
487 // Create an output node for each group, giving each a unique name
488 for (unsigned int g = 0; g < numGroups; ++g)
telsoa014fcda012018-03-09 14:13:49 +0000489 {
telsoa01c577f2c2018-08-31 09:22:23 +0100490 // Work out the names of the splitter layers child convolutions
491 stringstream ss;
492 ss << layerParam.name() << "_" << g;
493 convLayerNames[g] = ss.str();
telsoa014fcda012018-03-09 14:13:49 +0000494
telsoa01c577f2c2018-08-31 09:22:23 +0100495 splitterDesc.SetViewOriginCoord(g, 1, splitterDimSizes[1] * g);
telsoa014fcda012018-03-09 14:13:49 +0000496
telsoa01c577f2c2018-08-31 09:22:23 +0100497 // Set the size of the views.
498 for (unsigned int dimIdx=0; dimIdx < 4; dimIdx++)
telsoa014fcda012018-03-09 14:13:49 +0000499 {
telsoa01c577f2c2018-08-31 09:22:23 +0100500 splitterDesc.SetViewSize(g, dimIdx, splitterDimSizes[dimIdx]);
telsoa014fcda012018-03-09 14:13:49 +0000501 }
502 }
503
telsoa01c577f2c2018-08-31 09:22:23 +0100504 const std::string splitterLayerName = std::string("splitter_") + layerParam.bottom(0);
505 armnn::IConnectableLayer* splitterLayer = m_Network->AddSplitterLayer(splitterDesc, splitterLayerName.c_str());
telsoa014fcda012018-03-09 14:13:49 +0000506
telsoa01c577f2c2018-08-31 09:22:23 +0100507 inputConnection.Connect(splitterLayer->GetInputSlot(0));
508 for (unsigned int i = 0; i < splitterLayer->GetNumOutputSlots(); i++)
509 {
510 splitterLayer->GetOutputSlot(i).SetTensorInfo(BlobShapeToTensorInfo(inputShape));
511 }
telsoa014fcda012018-03-09 14:13:49 +0000512
513 unsigned int numFilters = convParam.num_output();
514
telsoa01c577f2c2018-08-31 09:22:23 +0100515 // Populates convolution output tensor descriptor dimensions.
telsoa014fcda012018-03-09 14:13:49 +0000516 BlobShape outputShape;
517 outputShape.add_dim(0);
518 outputShape.set_dim(0, inputShape.dim(0));
519 outputShape.add_dim(1);
telsoa01c577f2c2018-08-31 09:22:23 +0100520 // Ensures that dimension 1 of the convolution output is split according to the number of groups.
telsoa014fcda012018-03-09 14:13:49 +0000521 outputShape.set_dim(1, numFilters / numGroups);
522 outputShape.add_dim(2);
523 outputShape.set_dim(
telsoa01c577f2c2018-08-31 09:22:23 +0100524 2, (static_cast<int>(
Keith Mok7dc18202020-12-20 13:45:51 -0800525 static_cast<float>(inputShape.dim(2) + 2 * desc.m_PadBottom - (desc.m_DilationX * (kernelH - 1) + 1)) /
telsoa01c577f2c2018-08-31 09:22:23 +0100526 static_cast<float>(desc.m_StrideY)) + 1));
telsoa014fcda012018-03-09 14:13:49 +0000527 outputShape.add_dim(3);
528 outputShape.set_dim(
telsoa01c577f2c2018-08-31 09:22:23 +0100529 3, (static_cast<int>(
Keith Mok7dc18202020-12-20 13:45:51 -0800530 static_cast<float>(inputShape.dim(3) + 2 * desc.m_PadRight - (desc.m_DilationY * (kernelW - 1) + 1)) /
telsoa01c577f2c2018-08-31 09:22:23 +0100531 static_cast<float>(desc.m_StrideX)) + 1));
telsoa014fcda012018-03-09 14:13:49 +0000532
533 // Load the weight data for ALL groups
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100534 vector<float> weightData(armnn::numeric_cast<size_t>(numGroups *
telsoa01c577f2c2018-08-31 09:22:23 +0100535 inputShape.dim(1) * // number of input channels
536 outputShape.dim(1) * // number of output channels
537 kernelH *
538 kernelW));
telsoa014fcda012018-03-09 14:13:49 +0000539 GetDataFromBlob(layerParam, weightData, 0);
540
541 const unsigned int weightDimSizes[4] = {
telsoa01c577f2c2018-08-31 09:22:23 +0100542 static_cast<unsigned int>(outputShape.dim(1)),
543 static_cast<unsigned int>(inputShape.dim(1)),
544 kernelH,
545 kernelW};
telsoa014fcda012018-03-09 14:13:49 +0000546
telsoa014fcda012018-03-09 14:13:49 +0000547 TensorInfo biasInfo;
548 vector<float> biasData;
telsoa01c577f2c2018-08-31 09:22:23 +0100549
550 if (desc.m_BiasEnabled)
telsoa014fcda012018-03-09 14:13:49 +0000551 {
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100552 biasData.resize(armnn::numeric_cast<size_t>(numGroups * outputShape.dim(1)), 1.f);
telsoa014fcda012018-03-09 14:13:49 +0000553 GetDataFromBlob(layerParam, biasData, 1);
554
555 const unsigned int biasDimSizes[1] = {static_cast<unsigned int>(outputShape.dim(1))};
556 biasInfo = TensorInfo(1, biasDimSizes, DataType::Float32);
557 }
558
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100559 const unsigned int numWeightsPerGroup = armnn::numeric_cast<unsigned int>(weightData.size()) / numGroups;
560 const unsigned int numBiasesPerGroup = armnn::numeric_cast<unsigned int>(biasData.size()) / numGroups;
telsoa014fcda012018-03-09 14:13:49 +0000561
telsoa014fcda012018-03-09 14:13:49 +0000562 for (unsigned int g = 0; g < numGroups; ++g)
563 {
telsoa01c577f2c2018-08-31 09:22:23 +0100564 // Sets the slot index, group 0 should be connected to the 0th output of the splitter
565 // group 1 should be connected to the 1st output of the splitter.
telsoa014fcda012018-03-09 14:13:49 +0000566
telsoa01c577f2c2018-08-31 09:22:23 +0100567 // Pulls out the weights for this group from that loaded from the model file earlier.
telsoa014fcda012018-03-09 14:13:49 +0000568 ConstTensor weights(TensorInfo(4, weightDimSizes, DataType::Float32),
569 weightData.data() + numWeightsPerGroup * g);
570
571 IConnectableLayer* convLayer = nullptr;
Matteo Martincighfc598e12019-05-14 10:36:13 +0100572 Optional<ConstTensor> optionalBiases;
telsoa01c577f2c2018-08-31 09:22:23 +0100573 if (desc.m_BiasEnabled)
telsoa014fcda012018-03-09 14:13:49 +0000574 {
telsoa01c577f2c2018-08-31 09:22:23 +0100575 // Pulls out the biases for this group from that loaded from the model file earlier.
telsoa014fcda012018-03-09 14:13:49 +0000576 ConstTensor biases(biasInfo, biasData.data() + numBiasesPerGroup * g);
Matteo Martincighfc598e12019-05-14 10:36:13 +0100577 optionalBiases = Optional<ConstTensor>(biases);
telsoa014fcda012018-03-09 14:13:49 +0000578 }
Matteo Martincighfc598e12019-05-14 10:36:13 +0100579 convLayer = m_Network->AddConvolution2dLayer(desc,
580 weights,
581 optionalBiases,
582 convLayerNames[g].c_str());
telsoa014fcda012018-03-09 14:13:49 +0000583 convLayers[g] = convLayer;
584
585 // If we have more than one group then the input to the nth convolution the splitter layer's nth output,
586 // otherwise it's the regular input to this layer.
telsoa01c577f2c2018-08-31 09:22:23 +0100587 armnn::IOutputSlot& splitterInputConnection =
588 splitterLayer ? splitterLayer->GetOutputSlot(g) : inputConnection;
telsoa014fcda012018-03-09 14:13:49 +0000589 splitterInputConnection.Connect(convLayer->GetInputSlot(0));
590 convLayer->GetOutputSlot(0).SetTensorInfo(BlobShapeToTensorInfo(outputShape));
telsoa014fcda012018-03-09 14:13:49 +0000591 }
592
Jim Flynne242f2d2019-05-22 14:24:13 +0100593 // If the convolution was performed in chunks, add a layer to concatenate the results
telsoa01c577f2c2018-08-31 09:22:23 +0100594
595 // The merge input shape matches that of the convolution output
Jim Flynne242f2d2019-05-22 14:24:13 +0100596 unsigned int concatDimSizes[4] = {static_cast<unsigned int>(outputShape.dim(0)),
597 static_cast<unsigned int>(outputShape.dim(1)),
598 static_cast<unsigned int>(outputShape.dim(2)),
599 static_cast<unsigned int>(outputShape.dim(3))};
telsoa01c577f2c2018-08-31 09:22:23 +0100600
Jim Flynne242f2d2019-05-22 14:24:13 +0100601 // This is used to describe how the input is to be concatenated
602 OriginsDescriptor concatDesc(numGroups);
telsoa01c577f2c2018-08-31 09:22:23 +0100603
604 // Now create an input node for each group, using the name from
605 // the output of the corresponding convolution
606 for (unsigned int g = 0; g < numGroups; ++g)
telsoa014fcda012018-03-09 14:13:49 +0000607 {
Jim Flynne242f2d2019-05-22 14:24:13 +0100608 concatDesc.SetViewOriginCoord(g, 1, concatDimSizes[1] * g);
telsoa01c577f2c2018-08-31 09:22:23 +0100609 }
telsoa014fcda012018-03-09 14:13:49 +0000610
Jim Flynne242f2d2019-05-22 14:24:13 +0100611 // Make sure the output from the concat is the correct size to hold the data for all groups
612 concatDimSizes[1] *= numGroups;
613 outputShape.set_dim(1, concatDimSizes[1]);
telsoa014fcda012018-03-09 14:13:49 +0000614
Jim Flynne242f2d2019-05-22 14:24:13 +0100615 // Finally add the concat layer
616 IConnectableLayer* concatLayer = m_Network->AddConcatLayer(concatDesc, layerParam.name().c_str());
telsoa014fcda012018-03-09 14:13:49 +0000617
Jim Flynne242f2d2019-05-22 14:24:13 +0100618 if (!concatLayer)
telsoa01c577f2c2018-08-31 09:22:23 +0100619 {
620 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100621 fmt::format("Failed to create final concat layer for Split+Convolution+Concat. "
622 "Layer={} #groups={} #filters={} {}",
623 layerParam.name(),
624 numGroups,
625 numFilters,
626 CHECK_LOCATION().AsString()));
telsoa01c577f2c2018-08-31 09:22:23 +0100627 }
telsoa014fcda012018-03-09 14:13:49 +0000628
telsoa01c577f2c2018-08-31 09:22:23 +0100629 for (unsigned int g = 0; g < numGroups; ++g)
630 {
Jim Flynne242f2d2019-05-22 14:24:13 +0100631 convLayers[g]->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(g));
telsoa01c577f2c2018-08-31 09:22:23 +0100632 }
Jim Flynne242f2d2019-05-22 14:24:13 +0100633 concatLayer->GetOutputSlot(0).SetTensorInfo(armnn::TensorInfo(4, concatDimSizes, DataType::Float32));
634 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), concatLayer->GetOutputSlot(0));
telsoa01c577f2c2018-08-31 09:22:23 +0100635}
telsoa014fcda012018-03-09 14:13:49 +0000636
Kevin Mayef33cb12021-01-29 14:24:57 +0000637void ICaffeParser::CaffeParserImpl::AddDeconvLayerWithSplits(const caffe::LayerParameter& layerParam,
638 const armnn::TransposeConvolution2dDescriptor& desc,
639 unsigned int kernelW,
640 unsigned int kernelH)
Keith Mok9801b172020-12-20 19:47:25 -0800641{
642 ARMNN_ASSERT(layerParam.type() == "Deconvolution");
643 ValidateNumInputsOutputs(layerParam, 1, 1);
644
645 ConvolutionParameter convParam = layerParam.convolution_param();
646 BlobShape inputShape = TensorDescToBlobShape(GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo());
647 const unsigned int numGroups = convParam.has_group() ? convParam.group() : 1;
648
649 // asusme these were already verified by the caller ParseDeconvLayer() function
650 ARMNN_ASSERT(numGroups <= inputShape.dim(1));
651 ARMNN_ASSERT(numGroups > 1);
652
653 // Handle grouping
654 armnn::IOutputSlot& inputConnection = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0));
655
656 vector<string> convLayerNames(numGroups);
657 vector<armnn::IConnectableLayer*> convLayers(numGroups);
658 convLayerNames[0] = layerParam.name();
659
660 // This deconvolution is to be applied to chunks of the input data so add a splitter layer
661
662 // Redirect the deconvolution input to the splitter
663 unsigned int splitterDimSizes[4] = {static_cast<unsigned int>(inputShape.dim(0)),
664 static_cast<unsigned int>(inputShape.dim(1)),
665 static_cast<unsigned int>(inputShape.dim(2)),
666 static_cast<unsigned int>(inputShape.dim(3))};
667
668 // Split dimension 1 of the splitter output shape and deconv input shapes
669 // according to the number of groups
670
671 splitterDimSizes[1] /= numGroups;
672 inputShape.set_dim(1, splitterDimSizes[1]);
673
674 // This is used to describe how the input is to be split
675 ViewsDescriptor splitterDesc(numGroups);
676
677 // Create an output node for each group, giving each a unique name
678 for (unsigned int g = 0; g < numGroups; ++g)
679 {
680 // Work out the names of the splitter layers child deconvolutions
681 stringstream ss;
682 ss << layerParam.name() << "_" << g;
683 convLayerNames[g] = ss.str();
684
685 splitterDesc.SetViewOriginCoord(g, 1, splitterDimSizes[1] * g);
686
687 // Set the size of the views.
688 for (unsigned int dimIdx=0; dimIdx < 4; dimIdx++)
689 {
690 splitterDesc.SetViewSize(g, dimIdx, splitterDimSizes[dimIdx]);
691 }
692 }
693
694 const std::string splitterLayerName = std::string("splitter_") + layerParam.bottom(0);
695 armnn::IConnectableLayer* splitterLayer = m_Network->AddSplitterLayer(splitterDesc, splitterLayerName.c_str());
696
697 inputConnection.Connect(splitterLayer->GetInputSlot(0));
698 for (unsigned int i = 0; i < splitterLayer->GetNumOutputSlots(); i++)
699 {
700 splitterLayer->GetOutputSlot(i).SetTensorInfo(BlobShapeToTensorInfo(inputShape));
701 }
702
703 unsigned int numFilters = convParam.num_output();
704
705 // Populates deconvolution output tensor descriptor dimensions.
706 BlobShape outputShape;
707 outputShape.add_dim(0);
708 outputShape.set_dim(0, inputShape.dim(0));
709 outputShape.add_dim(1);
710 // Ensures that dimension 1 of the deconvolution output is split according to the number of groups.
711 outputShape.set_dim(1, numFilters / numGroups);
712 outputShape.add_dim(2);
713 outputShape.set_dim(
714 2, (static_cast<int>(
715 desc.m_StrideY * (inputShape.dim(2) - 1) - 2 * desc.m_PadBottom + kernelH)));
716 outputShape.add_dim(3);
717 outputShape.set_dim(
718 3, (static_cast<int>(
719 desc.m_StrideX * (inputShape.dim(3) - 1) - 2 * desc.m_PadRight + kernelW)));
720
721 // Load the weight data for ALL groups
722 vector<float> weightData(armnn::numeric_cast<size_t>(numGroups *
723 inputShape.dim(1) * // number of input channels
724 outputShape.dim(1) * // number of output channels
725 kernelH *
726 kernelW));
727 GetDataFromBlob(layerParam, weightData, 0);
728
729 const unsigned int weightDimSizes[4] = {
730 static_cast<unsigned int>(outputShape.dim(1)),
731 static_cast<unsigned int>(inputShape.dim(1)),
732 kernelH,
733 kernelW};
734
735 TensorInfo biasInfo;
736 vector<float> biasData;
737
738 if (desc.m_BiasEnabled)
739 {
740 biasData.resize(armnn::numeric_cast<size_t>(numGroups * outputShape.dim(1)), 1.f);
741 GetDataFromBlob(layerParam, biasData, 1);
742
743 const unsigned int biasDimSizes[1] = {static_cast<unsigned int>(outputShape.dim(1))};
744 biasInfo = TensorInfo(1, biasDimSizes, DataType::Float32);
745 }
746
747 const unsigned int numWeightsPerGroup = armnn::numeric_cast<unsigned int>(weightData.size()) / numGroups;
748 const unsigned int numBiasesPerGroup = armnn::numeric_cast<unsigned int>(biasData.size()) / numGroups;
749
750 for (unsigned int g = 0; g < numGroups; ++g)
751 {
752 // Sets the slot index, group 0 should be connected to the 0th output of the splitter
753 // group 1 should be connected to the 1st output of the splitter.
754
755 // Pulls out the weights for this group from that loaded from the model file earlier.
756 ConstTensor weights(TensorInfo(4, weightDimSizes, DataType::Float32),
757 weightData.data() + numWeightsPerGroup * g);
758
759 IConnectableLayer* deconvLayer = nullptr;
760 Optional<ConstTensor> optionalBiases;
761 if (desc.m_BiasEnabled)
762 {
763 // Pulls out the biases for this group from that loaded from the model file earlier.
764 ConstTensor biases(biasInfo, biasData.data() + numBiasesPerGroup * g);
765 optionalBiases = Optional<ConstTensor>(biases);
766 }
767 deconvLayer = m_Network->AddTransposeConvolution2dLayer(desc,
768 weights,
769 optionalBiases,
770 convLayerNames[g].c_str());
771 convLayers[g] = deconvLayer;
772
773 // If we have more than one group then the input to the nth deconvolution the splitter layer's nth output,
774 // otherwise it's the regular input to this layer.
775 armnn::IOutputSlot& splitterInputConnection =
776 splitterLayer ? splitterLayer->GetOutputSlot(g) : inputConnection;
777 splitterInputConnection.Connect(deconvLayer->GetInputSlot(0));
778 deconvLayer->GetOutputSlot(0).SetTensorInfo(BlobShapeToTensorInfo(outputShape));
779 }
780
781 // If the deconvolution was performed in chunks, add a layer to concatenate the results
782
783 // The merge input shape matches that of the deconvolution output
784 unsigned int concatDimSizes[4] = {static_cast<unsigned int>(outputShape.dim(0)),
785 static_cast<unsigned int>(outputShape.dim(1)),
786 static_cast<unsigned int>(outputShape.dim(2)),
787 static_cast<unsigned int>(outputShape.dim(3))};
788
789 // This is used to describe how the input is to be concatenated
790 OriginsDescriptor concatDesc(numGroups);
791
792 // Now create an input node for each group, using the name from
793 // the output of the corresponding deconvolution
794 for (unsigned int g = 0; g < numGroups; ++g)
795 {
796 concatDesc.SetViewOriginCoord(g, 1, concatDimSizes[1] * g);
797 }
798
799 // Make sure the output from the concat is the correct size to hold the data for all groups
800 concatDimSizes[1] *= numGroups;
801 outputShape.set_dim(1, concatDimSizes[1]);
802
803 // Finally add the concat layer
804 IConnectableLayer* concatLayer = m_Network->AddConcatLayer(concatDesc, layerParam.name().c_str());
805
806 if (!concatLayer)
807 {
808 throw ParseException(
809 fmt::format("Failed to create final concat layer for Split+Deconvolution+Concat. "
810 "Layer={} #groups={} #filters={} {}",
811 layerParam.name(),
812 numGroups,
813 numFilters,
814 CHECK_LOCATION().AsString()));
815 }
816
817 for (unsigned int g = 0; g < numGroups; ++g)
818 {
819 convLayers[g]->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(g));
820 }
821 concatLayer->GetOutputSlot(0).SetTensorInfo(armnn::TensorInfo(4, concatDimSizes, DataType::Float32));
822 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), concatLayer->GetOutputSlot(0));
823}
824
Kevin Mayef33cb12021-01-29 14:24:57 +0000825void ICaffeParser::CaffeParserImpl::AddConvLayerWithDepthwiseConv(const caffe::LayerParameter& layerParam,
826 const armnn::Convolution2dDescriptor& convDesc,
827 unsigned int kernelW,
828 unsigned int kernelH)
telsoa01c577f2c2018-08-31 09:22:23 +0100829{
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100830 ARMNN_ASSERT(layerParam.type() == "Convolution");
telsoa01c577f2c2018-08-31 09:22:23 +0100831 ValidateNumInputsOutputs(layerParam, 1, 1);
telsoa014fcda012018-03-09 14:13:49 +0000832
telsoa01c577f2c2018-08-31 09:22:23 +0100833 ConvolutionParameter convParam = layerParam.convolution_param();
834 BlobShape inputShape = TensorDescToBlobShape(GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo());
telsoa014fcda012018-03-09 14:13:49 +0000835
telsoa01c577f2c2018-08-31 09:22:23 +0100836 DepthwiseConvolution2dDescriptor desc;
837 desc.m_PadLeft = convDesc.m_PadLeft;
838 desc.m_PadRight = convDesc.m_PadRight;
839 desc.m_PadTop = convDesc.m_PadTop;
840 desc.m_PadBottom = convDesc.m_PadBottom;
841 desc.m_StrideX = convDesc.m_StrideX;
842 desc.m_StrideY = convDesc.m_StrideY;
Keith Mok7dc18202020-12-20 13:45:51 -0800843 desc.m_DilationX = convDesc.m_DilationX;
844 desc.m_DilationY = convDesc.m_DilationY;
telsoa01c577f2c2018-08-31 09:22:23 +0100845 desc.m_BiasEnabled = convDesc.m_BiasEnabled;
telsoa014fcda012018-03-09 14:13:49 +0000846
telsoa01c577f2c2018-08-31 09:22:23 +0100847 unsigned int numFilters = convParam.num_output();
848
849 BlobShape outputShape;
850 outputShape.add_dim(0);
851 outputShape.set_dim(0, inputShape.dim(0));
852 outputShape.add_dim(1);
853 outputShape.set_dim(1, numFilters);
854 outputShape.add_dim(2);
855 outputShape.set_dim(
856 2, (static_cast<int>(
Keith Mok7dc18202020-12-20 13:45:51 -0800857 static_cast<float>(inputShape.dim(2) + 2 * desc.m_PadBottom - (desc.m_DilationX * (kernelH - 1) + 1)) /
telsoa01c577f2c2018-08-31 09:22:23 +0100858 static_cast<float>(desc.m_StrideY)) + 1));
859 outputShape.add_dim(3);
860 outputShape.set_dim(
861 3, (static_cast<int>(
Keith Mok7dc18202020-12-20 13:45:51 -0800862 static_cast<float>(inputShape.dim(3) + 2 * desc.m_PadRight - (desc.m_DilationY * (kernelW - 1) + 1)) /
telsoa01c577f2c2018-08-31 09:22:23 +0100863 static_cast<float>(desc.m_StrideX)) + 1));
864
865 // Load the weight data
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100866 size_t allWeightsSize = armnn::numeric_cast<size_t>(inputShape.dim(1) * kernelH * kernelW);
telsoa01c577f2c2018-08-31 09:22:23 +0100867 vector<float> weightData(allWeightsSize);
868
869 GetDataFromBlob(layerParam, weightData, 0);
870
871 // depth multiplier will be 1 for the depthwise convolution
872 const unsigned int weightDimSizes[4] = {
873 static_cast<unsigned int>(1), // depth multiplier
874 static_cast<unsigned int>(inputShape.dim(1)), // #channels
875 kernelH,
876 kernelW};
877
878 armnn::IConnectableLayer* returnLayer = nullptr;
879 ConstTensor weights(TensorInfo(4, weightDimSizes, DataType::Float32), weightData.data());
Matteo Martincighfc598e12019-05-14 10:36:13 +0100880 Optional<ConstTensor> optionalBiases;
881 vector<float> biasData;
telsoa01c577f2c2018-08-31 09:22:23 +0100882 if (desc.m_BiasEnabled)
883 {
884 TensorInfo biasInfo;
telsoa01c577f2c2018-08-31 09:22:23 +0100885
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100886 biasData.resize(armnn::numeric_cast<size_t>(outputShape.dim(1)), 1.f);
telsoa01c577f2c2018-08-31 09:22:23 +0100887 GetDataFromBlob(layerParam, biasData, 1);
888
889 const unsigned int biasDimSizes[1] = {static_cast<unsigned int>(outputShape.dim(1))};
890 biasInfo = TensorInfo(1, biasDimSizes, DataType::Float32);
891
892 ConstTensor biases(biasInfo, biasData.data());
Matteo Martincighfc598e12019-05-14 10:36:13 +0100893 optionalBiases = Optional<ConstTensor>(biases);
telsoa01c577f2c2018-08-31 09:22:23 +0100894 }
Matteo Martincighfc598e12019-05-14 10:36:13 +0100895 returnLayer = m_Network->AddDepthwiseConvolution2dLayer(desc,
896 weights,
897 optionalBiases,
898 layerParam.name().c_str());
telsoa014fcda012018-03-09 14:13:49 +0000899
surmeh013537c2c2018-05-18 16:31:43 +0100900 if (!returnLayer)
901 {
telsoa01c577f2c2018-08-31 09:22:23 +0100902 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100903 fmt::format("Failed to create depthwise convolution layer. "
904 "Layer={} #filters={} {}",
905 layerParam.name(),
906 numFilters,
907 CHECK_LOCATION().AsString()));
telsoa01c577f2c2018-08-31 09:22:23 +0100908 }
909 armnn::IOutputSlot& inputConnection = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0));
910 inputConnection.Connect(returnLayer->GetInputSlot(0));
911 returnLayer->GetOutputSlot(0).SetTensorInfo(BlobShapeToTensorInfo(outputShape));
912 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), returnLayer->GetOutputSlot(0));
913}
914
Kevin Mayef33cb12021-01-29 14:24:57 +0000915void ICaffeParser::CaffeParserImpl::ParseConvLayer(const LayerParameter& layerParam)
telsoa01c577f2c2018-08-31 09:22:23 +0100916{
917 // Ignored Caffe Parameters
telsoa01c577f2c2018-08-31 09:22:23 +0100918 // * Weight Filler
919 // * Bias Filler
920 // * Engine
921 // * Force nd_im2col
922 // * Axis
923
924 // Not Available ArmNN Interface Parameters
925 // * Rounding policy;
926
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100927 ARMNN_ASSERT(layerParam.type() == "Convolution");
telsoa01c577f2c2018-08-31 09:22:23 +0100928 ValidateNumInputsOutputs(layerParam, 1, 1);
929
930 ConvolutionParameter convParam = layerParam.convolution_param();
931 BlobShape inputShape = TensorDescToBlobShape(GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo());
932 const unsigned int numGroups = convParam.has_group() ? convParam.group() : 1;
933 unsigned int numFilters = convParam.num_output();
934
935 const auto notFound = std::numeric_limits<unsigned int>::max();
936
937 unsigned int kernelH = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
938 kernel_h, kernel_size, unsigned int, notFound);
939 unsigned int kernelW = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
940 kernel_w, kernel_size, unsigned int, notFound);
941
942 unsigned int strideH = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
943 stride_h, stride, unsigned int, 1u);
944 unsigned int strideW = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
945 stride_w, stride, unsigned int, 1u);
946
947 unsigned int padH = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
948 pad_h, pad, unsigned int, 0u);
949 unsigned int padW = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
950 pad_w, pad, unsigned int, 0u);
951
Keith Mok7dc18202020-12-20 13:45:51 -0800952 unsigned int dilationH = convParam.dilation_size() > 0 ? convParam.dilation(0) : 1;
953 unsigned int dilationW = convParam.dilation_size() > 1 ? convParam.dilation(1) :
954 convParam.dilation_size() > 0 ? convParam.dilation(0) : 1;
955
telsoa01c577f2c2018-08-31 09:22:23 +0100956 Convolution2dDescriptor convolution2dDescriptor;
957 convolution2dDescriptor.m_PadLeft = padW;
958 convolution2dDescriptor.m_PadRight = padW;
959 convolution2dDescriptor.m_PadTop = padH;
960 convolution2dDescriptor.m_PadBottom = padH;
961 convolution2dDescriptor.m_StrideX = strideW;
962 convolution2dDescriptor.m_StrideY = strideH;
Keith Mok7dc18202020-12-20 13:45:51 -0800963 convolution2dDescriptor.m_DilationX = dilationW;
964 convolution2dDescriptor.m_DilationY = dilationH;
telsoa01c577f2c2018-08-31 09:22:23 +0100965 convolution2dDescriptor.m_BiasEnabled = convParam.has_bias_term() ? convParam.bias_term() : true;
966
967 if (numGroups > numFilters)
968 {
969 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100970 fmt::format("Error parsing Convolution: {}. "
971 "The 'group'={} parameter cannot be larger than the "
972 "number of filters supplied ='{}'. {}",
973 layerParam.name(),
974 numGroups,
975 numFilters,
976 CHECK_LOCATION().AsString()));
telsoa01c577f2c2018-08-31 09:22:23 +0100977 }
978
979 if (inputShape.dim_size() != 4)
980 {
981 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100982 fmt::format("Convolution input shape is expected to have 4 dimensions. "
983 "{}'s input has only {}. {}",
984 layerParam.name(),
985 inputShape.dim_size(),
986 CHECK_LOCATION().AsString()));
telsoa01c577f2c2018-08-31 09:22:23 +0100987 }
988
989 if (numGroups > 1)
990 {
991 if (numGroups > inputShape.dim(1))
992 {
993 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100994 fmt::format("Error parsing Convolution: {}. "
995 "The 'group'={} parameter cannot be larger than the "
996 "channel of the input shape={} (in NCHW format). {}",
997 layerParam.name(),
998 numGroups,
999 inputShape.dim(1),
1000 CHECK_LOCATION().AsString()));
telsoa01c577f2c2018-08-31 09:22:23 +01001001 }
1002 else if (numGroups == inputShape.dim(1))
1003 {
1004 // we use a depthwise convolution here, because the number of groups equals to the
1005 // input channels
1006 AddConvLayerWithDepthwiseConv(layerParam, convolution2dDescriptor, kernelW, kernelH);
1007 return;
1008 }
1009 else
1010 {
1011 // we split the input by channels into channels/groups separate convolutions
Jim Flynne242f2d2019-05-22 14:24:13 +01001012 // and concatenate the results afterwards
telsoa01c577f2c2018-08-31 09:22:23 +01001013 AddConvLayerWithSplits(layerParam, convolution2dDescriptor, kernelW, kernelH);
1014 return;
1015 }
1016 }
1017
1018 // NOTE: at this point we only need to handle #group=1 case, all other cases should be
1019 // handled by the AddConvLayer* helpers
1020
1021 // Populate convolution output tensor descriptor dimensions
1022 BlobShape outputShape;
1023 outputShape.add_dim(0);
1024 outputShape.set_dim(0, inputShape.dim(0));
1025 outputShape.add_dim(1);
1026 outputShape.set_dim(1, numFilters);
1027 outputShape.add_dim(2);
1028 outputShape.set_dim(
1029 2, (static_cast<int>(
Keith Mok7dc18202020-12-20 13:45:51 -08001030 static_cast<float>(inputShape.dim(2) + 2 * padH - (dilationH * (kernelH - 1) + 1)) /
telsoa01c577f2c2018-08-31 09:22:23 +01001031 static_cast<float>(strideH)) + 1));
1032 outputShape.add_dim(3);
1033 outputShape.set_dim(
1034 3, (static_cast<int>(
Keith Mok7dc18202020-12-20 13:45:51 -08001035 static_cast<float>(inputShape.dim(3) + 2 * padW - (dilationW * (kernelW - 1) + 1)) /
telsoa01c577f2c2018-08-31 09:22:23 +01001036 static_cast<float>(strideW)) + 1));
1037
1038 // Load the weight data for ALL groups
Matthew Sloyan589e3e82020-09-11 16:17:48 +01001039 vector<float> weightData(armnn::numeric_cast<size_t>(inputShape.dim(1) *
telsoa01c577f2c2018-08-31 09:22:23 +01001040 outputShape.dim(1) *
1041 kernelH *
1042 kernelW));
1043 GetDataFromBlob(layerParam, weightData, 0);
1044
1045 const unsigned int weightDimSizes[4] = {
1046 static_cast<unsigned int>(outputShape.dim(1)), // output channels
1047 static_cast<unsigned int>(inputShape.dim(1)), // input channels
1048 kernelH,
1049 kernelW};
1050
1051 armnn::IConnectableLayer* returnLayer = nullptr;
1052
1053 // Pull out the weights for this group from that loaded from the model file earlier
1054 ConstTensor weights(TensorInfo(4, weightDimSizes, DataType::Float32), weightData.data());
Matteo Martincighfc598e12019-05-14 10:36:13 +01001055 Optional<ConstTensor> optionalBiases;
1056 vector<float> biasData;
telsoa01c577f2c2018-08-31 09:22:23 +01001057 if (convolution2dDescriptor.m_BiasEnabled)
1058 {
1059 TensorInfo biasInfo;
telsoa01c577f2c2018-08-31 09:22:23 +01001060
Matthew Sloyan589e3e82020-09-11 16:17:48 +01001061 biasData.resize(armnn::numeric_cast<size_t>(outputShape.dim(1)), 1.f);
telsoa01c577f2c2018-08-31 09:22:23 +01001062 GetDataFromBlob(layerParam, biasData, 1);
1063
1064 const unsigned int biasDimSizes[1] = {static_cast<unsigned int>(outputShape.dim(1))};
1065 biasInfo = TensorInfo(1, biasDimSizes, DataType::Float32);
1066
1067 // Pull out the biases for this group from that loaded from the model file earlier
1068 ConstTensor biases(biasInfo, biasData.data());
Matteo Martincighfc598e12019-05-14 10:36:13 +01001069 optionalBiases = Optional<ConstTensor>(biases);
telsoa01c577f2c2018-08-31 09:22:23 +01001070 }
Matteo Martincighfc598e12019-05-14 10:36:13 +01001071 returnLayer = m_Network->AddConvolution2dLayer(convolution2dDescriptor,
1072 weights,
1073 optionalBiases,
1074 layerParam.name().c_str());
telsoa01c577f2c2018-08-31 09:22:23 +01001075
1076 armnn::IOutputSlot& inputConnection = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0));
1077 inputConnection.Connect(returnLayer->GetInputSlot(0));
1078 returnLayer->GetOutputSlot(0).SetTensorInfo(BlobShapeToTensorInfo(outputShape));
1079
1080 if (!returnLayer)
1081 {
1082 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001083 fmt::format("Failed to create Convolution layer. "
1084 "Layer={} #groups={} #filters={} {}",
1085 layerParam.name(),
1086 numGroups,
1087 numFilters,
1088 CHECK_LOCATION().AsString()));
surmeh013537c2c2018-05-18 16:31:43 +01001089 }
1090
telsoa014fcda012018-03-09 14:13:49 +00001091 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), returnLayer->GetOutputSlot(0));
1092}
1093
Kevin Mayef33cb12021-01-29 14:24:57 +00001094void ICaffeParser::CaffeParserImpl::ParseDeconvLayer(const LayerParameter& layerParam)
Keith Mok9801b172020-12-20 19:47:25 -08001095{
1096 // Ignored Caffe Parameters
1097 // * Weight Filler
1098 // * Bias Filler
1099 // * Engine
1100 // * Force nd_im2col
1101 // * Axis
1102
1103 // Not Available ArmNN Interface Parameters
1104 // * Rounding policy;
1105
1106 ARMNN_ASSERT(layerParam.type() == "Deconvolution");
1107 ValidateNumInputsOutputs(layerParam, 1, 1);
1108
1109 ConvolutionParameter convParam = layerParam.convolution_param();
1110 BlobShape inputShape = TensorDescToBlobShape(GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo());
1111 const unsigned int numGroups = convParam.has_group() ? convParam.group() : 1;
1112 unsigned int numFilters = convParam.num_output();
1113
1114 const auto notFound = std::numeric_limits<unsigned int>::max();
1115
1116 unsigned int kernelH = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
1117 kernel_h, kernel_size, unsigned int, notFound);
1118 unsigned int kernelW = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
1119 kernel_w, kernel_size, unsigned int, notFound);
1120
1121 unsigned int strideH = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
1122 stride_h, stride, unsigned int, 1u);
1123 unsigned int strideW = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
1124 stride_w, stride, unsigned int, 1u);
1125
1126 unsigned int padH = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
1127 pad_h, pad, unsigned int, 0u);
1128 unsigned int padW = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
1129 pad_w, pad, unsigned int, 0u);
1130
1131 unsigned int dilationH = convParam.dilation_size() > 0 ? convParam.dilation(0) : 1;
1132 unsigned int dilationW = convParam.dilation_size() > 1 ? convParam.dilation(1) :
1133 convParam.dilation_size() > 0 ? convParam.dilation(0) : 1;
1134
1135 if (dilationH != 1 || dilationW != 1) {
1136 fmt::format("Dilated decnvolution is not supported. "
1137 "{}'s input has dilation {} {}. {}",
1138 layerParam.name(),
1139 dilationW, dilationH,
1140 CHECK_LOCATION().AsString());
1141 }
1142
1143 TransposeConvolution2dDescriptor deconvolution2dDescriptor;
1144 deconvolution2dDescriptor.m_PadLeft = padW;
1145 deconvolution2dDescriptor.m_PadRight = padW;
1146 deconvolution2dDescriptor.m_PadTop = padH;
1147 deconvolution2dDescriptor.m_PadBottom = padH;
1148 deconvolution2dDescriptor.m_StrideX = strideW;
1149 deconvolution2dDescriptor.m_StrideY = strideH;
1150 deconvolution2dDescriptor.m_BiasEnabled = convParam.has_bias_term() ? convParam.bias_term() : true;
1151
1152 if (numGroups > numFilters)
1153 {
1154 throw ParseException(
1155 fmt::format("Error parsing Deconvolution: {}. "
1156 "The 'group'={} parameter cannot be larger than the "
1157 "number of filters supplied ='{}'. {}",
1158 layerParam.name(),
1159 numGroups,
1160 numFilters,
1161 CHECK_LOCATION().AsString()));
1162 }
1163
1164 if (inputShape.dim_size() != 4)
1165 {
1166 throw ParseException(
1167 fmt::format("Deconvolution input shape is expected to have 4 dimensions. "
1168 "{}'s input has only {}. {}",
1169 layerParam.name(),
1170 inputShape.dim_size(),
1171 CHECK_LOCATION().AsString()));
1172 }
1173
1174 if (numGroups > 1)
1175 {
1176 if (numGroups > inputShape.dim(1))
1177 {
1178 throw ParseException(
1179 fmt::format("Error parsing Deconvolution: {}. "
1180 "The 'group'={} parameter cannot be larger than the "
1181 "channel of the input shape={} (in NCHW format). {}",
1182 layerParam.name(),
1183 numGroups,
1184 inputShape.dim(1),
1185 CHECK_LOCATION().AsString()));
1186 }
1187 else
1188 {
1189 // we split the input by channels into channels/groups separate convolutions
1190 // and concatenate the results afterwards
1191 AddDeconvLayerWithSplits(layerParam, deconvolution2dDescriptor, kernelW, kernelH);
1192 return;
1193 }
1194 }
1195
1196 // NOTE: at this point we only need to handle #group=1 case, all other cases should be
1197 // handled by the AddDeconvLayer* helpers
1198
1199 // Populate deconvolution output tensor descriptor dimensions
1200 BlobShape outputShape;
1201 outputShape.add_dim(0);
1202 outputShape.set_dim(0, inputShape.dim(0));
1203 outputShape.add_dim(1);
1204 outputShape.set_dim(1, numFilters);
1205 outputShape.add_dim(2);
1206 outputShape.set_dim(
1207 2, (static_cast<int>(
1208 strideH * (inputShape.dim(2) - 1) - 2 * padH + (dilationH * (kernelH - 1) + 1))));
1209 outputShape.add_dim(3);
1210 outputShape.set_dim(
1211 3, (static_cast<int>(
1212 strideW * (inputShape.dim(3) - 1) - 2 * padW + (dilationW * (kernelW - 1) + 1))));
1213
1214 // Load the weight data for ALL groups
1215 vector<float> weightData(armnn::numeric_cast<size_t>(inputShape.dim(1) *
1216 outputShape.dim(1) *
1217 kernelH *
1218 kernelW));
1219 GetDataFromBlob(layerParam, weightData, 0);
1220
1221 const unsigned int weightDimSizes[4] = {
1222 static_cast<unsigned int>(outputShape.dim(1)), // output channels
1223 static_cast<unsigned int>(inputShape.dim(1)), // input channels
1224 kernelH,
1225 kernelW};
1226
1227 armnn::IConnectableLayer* returnLayer = nullptr;
1228
1229 // Pull out the weights for this group from that loaded from the model file earlier
1230 ConstTensor weights(TensorInfo(4, weightDimSizes, DataType::Float32), weightData.data());
1231 Optional<ConstTensor> optionalBiases;
1232 vector<float> biasData;
1233 if (deconvolution2dDescriptor.m_BiasEnabled)
1234 {
1235 TensorInfo biasInfo;
1236
1237 biasData.resize(armnn::numeric_cast<size_t>(outputShape.dim(1)), 1.f);
1238 GetDataFromBlob(layerParam, biasData, 1);
1239
1240 const unsigned int biasDimSizes[1] = {static_cast<unsigned int>(outputShape.dim(1))};
1241 biasInfo = TensorInfo(1, biasDimSizes, DataType::Float32);
1242
1243 // Pull out the biases for this group from that loaded from the model file earlier
1244 ConstTensor biases(biasInfo, biasData.data());
1245 optionalBiases = Optional<ConstTensor>(biases);
1246 }
1247 returnLayer = m_Network->AddTransposeConvolution2dLayer(deconvolution2dDescriptor,
1248 weights,
1249 optionalBiases,
1250 layerParam.name().c_str());
1251
1252 armnn::IOutputSlot& inputConnection = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0));
1253 inputConnection.Connect(returnLayer->GetInputSlot(0));
1254 returnLayer->GetOutputSlot(0).SetTensorInfo(BlobShapeToTensorInfo(outputShape));
1255
1256 if (!returnLayer)
1257 {
1258 throw ParseException(
1259 fmt::format("Failed to create Deconvolution layer. "
1260 "Layer={} #groups={} #filters={} {}",
1261 layerParam.name(),
1262 numGroups,
1263 numFilters,
1264 CHECK_LOCATION().AsString()));
1265 }
1266
1267 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), returnLayer->GetOutputSlot(0));
1268}
1269
Kevin Mayef33cb12021-01-29 14:24:57 +00001270void ICaffeParser::CaffeParserImpl::ParsePoolingLayer(const LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +00001271{
telsoa01c577f2c2018-08-31 09:22:23 +01001272 // Ignored Caffe Parameters
1273 // Stochastic Pooling
1274 // Engine
1275
telsoa014fcda012018-03-09 14:13:49 +00001276 ValidateNumInputsOutputs(layerParam, 1, 1);
telsoa014fcda012018-03-09 14:13:49 +00001277 PoolingParameter param = layerParam.pooling_param();
telsoa014fcda012018-03-09 14:13:49 +00001278 const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo();
1279
telsoa01c577f2c2018-08-31 09:22:23 +01001280 const auto notFound = std::numeric_limits<unsigned int>::max();
1281
1282 unsigned int kernel_h = GET_OPTIONAL_WITH_FALLBACK(param, PoolingParameter,
1283 kernel_h, kernel_size, unsigned int, notFound);
1284 unsigned int kernel_w = GET_OPTIONAL_WITH_FALLBACK(param, PoolingParameter,
1285 kernel_w, kernel_size, unsigned int, notFound);
1286
1287 if ((kernel_h == notFound || kernel_w == notFound) && param.has_global_pooling())
telsoa014fcda012018-03-09 14:13:49 +00001288 {
1289 kernel_h = inputInfo.GetShape()[2];
1290 kernel_w = inputInfo.GetShape()[3];
1291 }
telsoa01c577f2c2018-08-31 09:22:23 +01001292
telsoa01c577f2c2018-08-31 09:22:23 +01001293 unsigned int stride_h = GET_OPTIONAL_WITH_FALLBACK(param, PoolingParameter,
1294 stride_h, stride, unsigned int, notFound);
1295 unsigned int stride_w = GET_OPTIONAL_WITH_FALLBACK(param, PoolingParameter,
1296 stride_h, stride, unsigned int, notFound);
1297
1298 if ((stride_h == notFound || stride_w == notFound) && param.has_global_pooling())
telsoa014fcda012018-03-09 14:13:49 +00001299 {
telsoa01c577f2c2018-08-31 09:22:23 +01001300 stride_h = 1;
1301 stride_w = 1;
telsoa014fcda012018-03-09 14:13:49 +00001302 }
1303
telsoa01c577f2c2018-08-31 09:22:23 +01001304 unsigned int pad_h = GET_OPTIONAL_WITH_FALLBACK(param, PoolingParameter,
1305 pad_h, pad, unsigned int, 0u);
1306 unsigned int pad_w = GET_OPTIONAL_WITH_FALLBACK(param, PoolingParameter,
1307 pad_w, pad, unsigned int, 0u);
telsoa014fcda012018-03-09 14:13:49 +00001308
telsoa014fcda012018-03-09 14:13:49 +00001309 // Populate Weight and Bias Filter Descriptor
1310 Pooling2dDescriptor pooling2dDescriptor;
1311 if (param.has_pool())
1312 {
1313 PoolingParameter_PoolMethod p = param.pool();
1314 switch (p)
1315 {
1316 case PoolingParameter_PoolMethod_MAX:
1317 {
1318 pooling2dDescriptor.m_PoolType = PoolingAlgorithm::Max;
1319 break;
1320 }
1321 case PoolingParameter_PoolMethod_AVE:
1322 {
1323 pooling2dDescriptor.m_PoolType = PoolingAlgorithm::Average;
1324 break;
1325 }
1326 case PoolingParameter_PoolMethod_STOCHASTIC:
1327 {
telsoa01c577f2c2018-08-31 09:22:23 +01001328 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001329 fmt::format("Pooling Layer: Stochastic Pooling Not Supported. Layer={} {}",
1330 layerParam.name(),
1331 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001332 }
1333 default:
1334 {
telsoa01c577f2c2018-08-31 09:22:23 +01001335 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001336 fmt::format("Pooling Layer: unknown pooling method: {} for layer: {} {}",
1337 p,
1338 layerParam.name(),
1339 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001340 }
1341 }
1342 }
1343 else
1344 {
telsoa01c577f2c2018-08-31 09:22:23 +01001345 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001346 fmt::format("No Pooling Method Defined for {} {}",
1347 layerParam.name(),
1348 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001349 }
1350
1351 pooling2dDescriptor.m_PadLeft = pad_w;
1352 pooling2dDescriptor.m_PadRight = pad_w;
1353 pooling2dDescriptor.m_PadTop = pad_h;
1354 pooling2dDescriptor.m_PadBottom = pad_h;
1355 pooling2dDescriptor.m_StrideX = stride_w;
1356 pooling2dDescriptor.m_StrideY = stride_h;
1357 pooling2dDescriptor.m_PoolWidth = kernel_w;
1358 pooling2dDescriptor.m_PoolHeight = kernel_h;
1359
1360 pooling2dDescriptor.m_OutputShapeRounding = OutputShapeRounding::Ceiling;
1361 pooling2dDescriptor.m_PaddingMethod = PaddingMethod::IgnoreValue;
1362
1363 armnn::IConnectableLayer* poolingLayer = m_Network->AddPooling2dLayer(pooling2dDescriptor,
1364 layerParam.name().c_str());
1365
telsoa014fcda012018-03-09 14:13:49 +00001366 TensorInfo outputInfo(
1367 { inputInfo.GetShape()[0],
1368 inputInfo.GetShape()[1],
1369 static_cast<unsigned int>(ceil(
1370 static_cast<float>(inputInfo.GetShape()[2] + 2 * pad_h - kernel_h) /
Matthew Sloyan24ac8592020-09-23 16:57:23 +01001371 armnn::numeric_cast<float>(stride_h))) + 1,
telsoa014fcda012018-03-09 14:13:49 +00001372 static_cast<unsigned int>(ceil(
1373 static_cast<float>(inputInfo.GetShape()[3] + 2 * pad_w - kernel_w) /
Matthew Sloyan24ac8592020-09-23 16:57:23 +01001374 armnn::numeric_cast<float>(stride_w))) + 1 },
telsoa014fcda012018-03-09 14:13:49 +00001375 DataType::Float32);
1376
1377 GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).Connect(poolingLayer->GetInputSlot(0));
1378 poolingLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1379 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), poolingLayer->GetOutputSlot(0));
1380}
1381
Kevin Mayef33cb12021-01-29 14:24:57 +00001382void ICaffeParser::CaffeParserImpl::ParseArgmaxLayer(const LayerParameter& layerParam)
Keith Mok9801b172020-12-20 19:47:25 -08001383{
1384 ValidateNumInputsOutputs(layerParam, 1, 1);
1385 ArgMaxParameter param = layerParam.argmax_param();
1386
1387 BlobShape inputShape = TensorDescToBlobShape(GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo());
1388
1389 const unsigned int topK = param.has_top_k() ? param.top_k() : 1;
1390 if (topK != 1) {
1391 throw ParseException(
1392 fmt::format("ArgMaxLayer: Only support top_k equals to 1. Layer={} {}",
1393 layerParam.name(),
1394 CHECK_LOCATION().AsString()));
1395 }
1396
1397 const unsigned int outMaxVal = param.has_out_max_val() ? param.out_max_val() : false;
1398 if (outMaxVal) {
1399 throw ParseException(
1400 fmt::format("ArgMaxLayer: Does not support out_max_val. Layer={} {}",
1401 layerParam.name(),
1402 CHECK_LOCATION().AsString()));
1403 }
1404
1405 int axis = param.has_axis() ? param.axis() : 1;
1406 if (axis < 0) {
1407 axis = inputShape.dim_size() - axis;
1408 }
1409 if ((axis < 0) || (axis >= inputShape.dim_size())) {
1410 throw ParseException(
1411 fmt::format("ArgMaxLayer: Invalid axis value which outside range of input dims. "
1412 "{}'s input has input dim_size {}, requested axis: {}. {}",
1413 layerParam.name(),
1414 inputShape.dim_size(),
1415 axis,
1416 CHECK_LOCATION().AsString()));
1417 }
1418
1419 ArgMinMaxDescriptor desc;
1420 desc.m_Axis = axis;
1421 desc.m_Output_Type = armnn::DataType::Signed32;
1422 desc.m_Function = ArgMinMaxFunction::Max;
1423
1424 armnn::IConnectableLayer* argmaxLayer = m_Network->AddArgMinMaxLayer(desc,
1425 layerParam.name().c_str());
1426
1427 TensorShape outputShape(static_cast<unsigned int>(inputShape.dim_size() - 1));
1428 int j = 0;
1429 // remove the flatten axis
1430 for (int i = 0; i < inputShape.dim_size(); ++i)
1431 {
1432 if (i == axis) continue;
1433 outputShape[static_cast<unsigned int>(j++)] = static_cast<unsigned int>(inputShape.dim(i));
1434 }
1435 TensorInfo outputInfo(outputShape, DataType::Signed32);
1436
1437 GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).Connect(argmaxLayer->GetInputSlot(0));
1438 argmaxLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1439 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), argmaxLayer->GetOutputSlot(0));
1440}
1441
Kevin Mayef33cb12021-01-29 14:24:57 +00001442void ICaffeParser::CaffeParserImpl::ParseReluLayer(const LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +00001443{
1444 ValidateNumInputsOutputs(layerParam, 1, 1);
1445
1446 const string& name = layerParam.name();
1447 const ReLUParameter& param = layerParam.relu_param();
1448
1449 ActivationDescriptor activationDescriptor;
1450 const float negativeSlope = param.negative_slope();
1451 if (negativeSlope == 0.0f)
1452 {
1453 activationDescriptor.m_Function = ActivationFunction::ReLu;
1454 }
1455 else
1456 {
1457 activationDescriptor.m_Function = ActivationFunction::LeakyReLu;
1458 activationDescriptor.m_A = negativeSlope;
1459 }
1460
1461 const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo();
1462 IConnectableLayer* const activationLayer = m_Network->AddActivationLayer(activationDescriptor, name.c_str());
1463 GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).Connect(activationLayer->GetInputSlot(0));
1464 activationLayer->GetOutputSlot(0).SetTensorInfo(inputInfo);
1465 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), activationLayer->GetOutputSlot(0));
1466}
1467
Kevin Mayef33cb12021-01-29 14:24:57 +00001468void ICaffeParser::CaffeParserImpl::ParseLRNLayer(const LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +00001469{
1470 ValidateNumInputsOutputs(layerParam, 1, 1);
1471
1472 LRNParameter param = layerParam.lrn_param();
1473
1474 const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo();
1475
telsoa01c577f2c2018-08-31 09:22:23 +01001476 // Ignored BATCH NORMALIZATION Caffe Parameters.
1477 // Ignored MVN Caffe Parameters.
1478 // Ignored LRN Caffe Parameters.
telsoa014fcda012018-03-09 14:13:49 +00001479 // Engine
1480
1481 NormalizationDescriptor normalizationDescriptor;
1482 if (param.has_norm_region())
1483 {
1484 LRNParameter_NormRegion n = param.norm_region();
1485 switch (n)
1486 {
1487 case LRNParameter_NormRegion_ACROSS_CHANNELS:
1488 {
1489 normalizationDescriptor.m_NormChannelType = NormalizationAlgorithmChannel::Across;
1490 break;
1491 }
1492 case LRNParameter_NormRegion_WITHIN_CHANNEL:
1493 {
1494 normalizationDescriptor.m_NormChannelType = NormalizationAlgorithmChannel::Within;
1495 break;
1496 }
1497 default:
telsoa01c577f2c2018-08-31 09:22:23 +01001498 {
1499 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001500 fmt::format("Unknown region {} for LRN layer {} {}",
1501 n,
1502 layerParam.name(),
1503 CHECK_LOCATION().AsString()));
telsoa01c577f2c2018-08-31 09:22:23 +01001504 }
telsoa014fcda012018-03-09 14:13:49 +00001505 }
1506 }
1507 else
1508 {
telsoa01c577f2c2018-08-31 09:22:23 +01001509 // Caffe defaults to normalization across channels.
telsoa014fcda012018-03-09 14:13:49 +00001510 normalizationDescriptor.m_NormChannelType = NormalizationAlgorithmChannel::Across;
1511 }
1512
1513 normalizationDescriptor.m_NormMethodType = NormalizationAlgorithmMethod::LocalBrightness;
1514 if (param.has_local_size())
1515 {
1516 normalizationDescriptor.m_NormSize = param.local_size();
1517 }
1518 else
1519 {
telsoa01c577f2c2018-08-31 09:22:23 +01001520 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001521 fmt::format("local_size not defined for LRN layer {} {}",
1522 layerParam.name(),
1523 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001524 }
1525
1526 if (param.has_alpha())
1527 {
1528 normalizationDescriptor.m_Alpha = param.alpha();
Matthew Sloyan24ac8592020-09-23 16:57:23 +01001529 normalizationDescriptor.m_Alpha /= armnn::numeric_cast<float>(param.local_size());
telsoa014fcda012018-03-09 14:13:49 +00001530 }
1531 else
1532 {
telsoa01c577f2c2018-08-31 09:22:23 +01001533 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001534 fmt::format("Alpha not defined for LRN layer {} {}",
1535 layerParam.name(),
1536 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001537 }
1538 if (param.has_beta())
1539 {
1540 normalizationDescriptor.m_Beta = param.beta();
1541 }
1542 else
1543 {
telsoa01c577f2c2018-08-31 09:22:23 +01001544 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001545 fmt::format("Beta not defined for LRN layer {} {}",
1546 layerParam.name(),
1547 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001548 }
telsoa01c577f2c2018-08-31 09:22:23 +01001549
telsoa014fcda012018-03-09 14:13:49 +00001550 if (param.has_k())
1551 {
1552 normalizationDescriptor.m_K = param.k();
1553 }
1554 else
telsoa01c577f2c2018-08-31 09:22:23 +01001555 {
telsoa014fcda012018-03-09 14:13:49 +00001556 normalizationDescriptor.m_K = 1;
telsoa01c577f2c2018-08-31 09:22:23 +01001557 }
telsoa014fcda012018-03-09 14:13:49 +00001558
1559 IConnectableLayer* const normLayer = m_Network->AddNormalizationLayer(normalizationDescriptor,
1560 layerParam.name().c_str());
1561 GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).Connect(normLayer->GetInputSlot(0));
1562 normLayer->GetOutputSlot(0).SetTensorInfo(inputInfo);
1563
1564 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), normLayer->GetOutputSlot(0));
1565}
1566
Kevin Mayef33cb12021-01-29 14:24:57 +00001567void ICaffeParser::CaffeParserImpl::ParseInnerProductLayer(const LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +00001568{
1569 InnerProductParameter param = layerParam.inner_product_param();
1570
1571 ValidateNumInputsOutputs(layerParam, 1, 1);
1572
1573 unsigned int outputSize = param.num_output();
1574
telsoa01c577f2c2018-08-31 09:22:23 +01001575 // Ignored Caffe Parameters:
telsoa014fcda012018-03-09 14:13:49 +00001576 // Weight Filler
1577 // Bias Filler
1578 // Engine
1579 // Axis
1580
1581 FullyConnectedDescriptor tensorFullyConnectedDescriptor;
1582
1583 if (param.has_transpose())
1584 {
telsoa01c577f2c2018-08-31 09:22:23 +01001585 // If true, assumes transposed weights.
telsoa014fcda012018-03-09 14:13:49 +00001586 tensorFullyConnectedDescriptor.m_TransposeWeightMatrix = param.transpose();
1587 }
1588 else
1589 {
telsoa01c577f2c2018-08-31 09:22:23 +01001590 // Caffe defaults to transposed.
telsoa014fcda012018-03-09 14:13:49 +00001591 tensorFullyConnectedDescriptor.m_TransposeWeightMatrix = true;
1592 }
1593
1594 const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo();
1595
1596 TensorInfo weightInfo;
1597 TensorInfo biasInfo;
1598
telsoa01c577f2c2018-08-31 09:22:23 +01001599 // Allows implicit flattening of extra dimensions.
telsoa014fcda012018-03-09 14:13:49 +00001600 unsigned int inputSize = inputInfo.GetShape()[1];
1601 for (unsigned int i = 2; i < inputInfo.GetNumDimensions(); ++i)
1602 {
1603 inputSize *= inputInfo.GetShape()[i];
1604 }
1605
telsoa01c577f2c2018-08-31 09:22:23 +01001606 const float* weightDataPtr = GetArrayPtrFromBlob(layerParam, 0);
telsoa014fcda012018-03-09 14:13:49 +00001607 const unsigned int swTD[2] = { outputSize, inputSize };
telsoa01c577f2c2018-08-31 09:22:23 +01001608 ConstTensor weights(TensorInfo(2, swTD, DataType::Float32), weightDataPtr);
telsoa014fcda012018-03-09 14:13:49 +00001609
1610 tensorFullyConnectedDescriptor.m_BiasEnabled = true;
telsoa01c577f2c2018-08-31 09:22:23 +01001611 // Todo: check whether bias enabled.
telsoa014fcda012018-03-09 14:13:49 +00001612 armnn::IConnectableLayer* fullyConnectedLayer = nullptr;
1613 if (tensorFullyConnectedDescriptor.m_BiasEnabled)
1614 {
1615 // BIAS VALUE
telsoa01c577f2c2018-08-31 09:22:23 +01001616 const float* biasDataPtr = GetArrayPtrFromBlob(layerParam, 1);
telsoa014fcda012018-03-09 14:13:49 +00001617
1618 const unsigned int sbTD[1] = { outputSize };
1619
telsoa01c577f2c2018-08-31 09:22:23 +01001620 ConstTensor biases(TensorInfo(1, sbTD, DataType::Float32), biasDataPtr);
telsoa014fcda012018-03-09 14:13:49 +00001621
Matteo Martincighfc598e12019-05-14 10:36:13 +01001622 fullyConnectedLayer = m_Network->AddFullyConnectedLayer(tensorFullyConnectedDescriptor,
1623 weights,
1624 Optional<ConstTensor>(biases),
1625 layerParam.name().c_str());
telsoa014fcda012018-03-09 14:13:49 +00001626 }
1627 else
1628 {
Matteo Martincighfc598e12019-05-14 10:36:13 +01001629 fullyConnectedLayer = m_Network->AddFullyConnectedLayer(tensorFullyConnectedDescriptor,
1630 weights,
1631 EmptyOptional(),
1632 layerParam.name().c_str());
telsoa014fcda012018-03-09 14:13:49 +00001633 }
1634
1635 TensorInfo outputInfo({ inputInfo.GetShape()[0], outputSize }, DataType::Float32);
1636 GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).Connect(fullyConnectedLayer->GetInputSlot(0));
1637 fullyConnectedLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1638 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), fullyConnectedLayer->GetOutputSlot(0));
1639}
1640
Kevin Mayef33cb12021-01-29 14:24:57 +00001641void ICaffeParser::CaffeParserImpl::ParseSoftmaxLayer(const LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +00001642{
1643 ValidateNumInputsOutputs(layerParam, 1, 1);
1644
1645 SoftmaxParameter param = layerParam.softmax_param();
1646
1647 const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo();
1648
telsoa01c577f2c2018-08-31 09:22:23 +01001649 // Ignored Caffe Parameters:
telsoa014fcda012018-03-09 14:13:49 +00001650 // axis
1651 // Engine
1652
1653 armnn::SoftmaxDescriptor softmaxDescriptor;
Teresa Charlin4320c922020-08-12 16:04:41 +01001654 softmaxDescriptor.m_Axis = 1;
telsoa014fcda012018-03-09 14:13:49 +00001655 armnn::IConnectableLayer* const softmaxLayer = m_Network->AddSoftmaxLayer(
1656 softmaxDescriptor,
1657 layerParam.name().c_str());
1658 GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).Connect(softmaxLayer->GetInputSlot(0));
1659 softmaxLayer->GetOutputSlot(0).SetTensorInfo(inputInfo);
1660 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), softmaxLayer->GetOutputSlot(0));
1661}
1662
Kevin Mayef33cb12021-01-29 14:24:57 +00001663void ICaffeParser::CaffeParserImpl::ParseEltwiseLayer(const LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +00001664{
1665 ValidateNumInputsOutputs(layerParam, 2, 1);
1666
1667 const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo();
1668
telsoa01c577f2c2018-08-31 09:22:23 +01001669 // Ignored Caffe Parameters:
telsoa014fcda012018-03-09 14:13:49 +00001670 // coeff
1671
telsoa01c577f2c2018-08-31 09:22:23 +01001672 EltwiseParameter_EltwiseOp operation = EltwiseParameter_EltwiseOp_SUM; // Defaults to sum as per caffe.
telsoa014fcda012018-03-09 14:13:49 +00001673
1674 if (layerParam.has_eltwise_param() && layerParam.eltwise_param().has_operation())
1675 {
1676 operation = layerParam.eltwise_param().operation();
1677 }
1678
1679 armnn::IConnectableLayer* newLayer = nullptr;
1680 switch (operation)
1681 {
1682 case EltwiseParameter_EltwiseOp_SUM:
1683 {
1684 newLayer = m_Network->AddAdditionLayer(layerParam.name().c_str());
1685 break;
1686 }
1687 case EltwiseParameter_EltwiseOp_PROD:
1688 {
1689 newLayer = m_Network->AddMultiplicationLayer(layerParam.name().c_str());
1690 break;
1691 }
1692 default:
1693 {
telsoa01c577f2c2018-08-31 09:22:23 +01001694 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001695 fmt::format("Unsupported operation {} in Eltwise layer {} {}",
1696 operation,
1697 layerParam.name(),
1698 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001699 }
1700 }
1701
1702 GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).Connect(newLayer->GetInputSlot(0));
1703 GetArmnnOutputSlotForCaffeTop(layerParam.bottom(1)).Connect(newLayer->GetInputSlot(1));
1704 newLayer->GetOutputSlot(0).SetTensorInfo(inputInfo);
1705 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), newLayer->GetOutputSlot(0));
1706}
1707
Kevin Mayef33cb12021-01-29 14:24:57 +00001708void ICaffeParser::CaffeParserImpl::ParseConcatLayer(const LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +00001709{
1710 unsigned int numInputs = static_cast<unsigned int>(layerParam.bottom_size());
telsoa01c577f2c2018-08-31 09:22:23 +01001711 // We assume concat happens along the channel dimension, which is 1 in (0, 1, 2, 3).
telsoa014fcda012018-03-09 14:13:49 +00001712 unsigned int concatDim = 1;
1713 unsigned int numOfDims = 4;
1714
telsoa01c577f2c2018-08-31 09:22:23 +01001715 // we only consider 4-D tensor here
1716 OriginsDescriptor concatDescriptor(static_cast<uint32_t>(numInputs), numOfDims);
telsoa014fcda012018-03-09 14:13:49 +00001717 std::vector<unsigned int>mergeDimSizes(numOfDims, 0u);
1718
1719 unsigned int mergeDim = 0;
1720 for (unsigned int viewIndex = 0; viewIndex < numInputs; ++viewIndex)
1721 {
1722 const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(
Matthew Sloyan589e3e82020-09-11 16:17:48 +01001723 layerParam.bottom(armnn::numeric_cast<int>(viewIndex))).GetTensorInfo();
telsoa01c577f2c2018-08-31 09:22:23 +01001724 // Checks whether the dimensions of the input tensors are actually 4.
telsoa014fcda012018-03-09 14:13:49 +00001725 if (inputInfo.GetNumDimensions()!=4)
1726 {
telsoa01c577f2c2018-08-31 09:22:23 +01001727 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001728 fmt::format("The number of dimensions for input tensors of "
1729 "the concatenation op should be 4. Inputs of {} has "
1730 "{} dimensions. {}",
1731 layerParam.name(),
1732 inputInfo.GetNumDimensions(),
1733 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001734 }
1735
1736 mergeDimSizes[0] = inputInfo.GetShape()[0];
1737 mergeDimSizes[1] = inputInfo.GetShape()[1];
1738 mergeDimSizes[2] = inputInfo.GetShape()[2];
1739 mergeDimSizes[3] = inputInfo.GetShape()[3];
1740
1741 for (unsigned int j = 0; j < concatDim; ++j)
1742 {
1743 concatDescriptor.SetViewOriginCoord(viewIndex, j, 0);
1744 }
1745
1746 concatDescriptor.SetViewOriginCoord(viewIndex, concatDim, mergeDim);
1747 mergeDim += mergeDimSizes[concatDim];
1748
1749 for (unsigned int j = concatDim+1; j < numOfDims; ++j)
1750 {
1751 concatDescriptor.SetViewOriginCoord(viewIndex, j, 0);
1752 }
1753 }
1754 mergeDimSizes[concatDim] = mergeDim;
1755
Jim Flynn906f9462019-05-10 13:55:21 +01001756 armnn::IConnectableLayer* concatlayer = m_Network->AddConcatLayer(concatDescriptor, layerParam.name().c_str());
telsoa014fcda012018-03-09 14:13:49 +00001757 for (unsigned int i = 0; i < numInputs; ++i)
1758 {
Matthew Sloyan589e3e82020-09-11 16:17:48 +01001759 armnn::IOutputSlot& outputSlot = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(armnn::numeric_cast<int>(i)));
telsoa014fcda012018-03-09 14:13:49 +00001760 outputSlot.Connect(concatlayer->GetInputSlot(i));
1761 }
1762
1763 concatlayer->GetOutputSlot(0).SetTensorInfo(armnn::TensorInfo(numOfDims, mergeDimSizes.data(), DataType::Float32));
1764 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), concatlayer->GetOutputSlot(0));
1765}
1766
Kevin Mayef33cb12021-01-29 14:24:57 +00001767void ICaffeParser::CaffeParserImpl::ParseBatchNormLayer(const LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +00001768{
1769 ValidateNumInputsOutputs(layerParam, 1, 1);
1770
1771 const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo();
1772
1773 string name = layerParam.name();
1774
1775 BatchNormParameter param = layerParam.batch_norm_param();
1776 // If use_global_stats is not explicitly set in the model, assume it to be true (its default value
1777 // when the network is in the testing phase).
1778 if (param.has_use_global_stats())
1779 {
1780 if (!param.use_global_stats())
1781 {
telsoa01c577f2c2018-08-31 09:22:23 +01001782 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001783 fmt::format("Error parsing Batch Norm layer '{}': "
1784 "Parameter 'use_global_stats' is set to false, which is "
1785 "unsupported (value used for training). {}",
1786 name,
1787 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001788 }
1789 }
1790
1791 BatchNormalizationDescriptor desc;
1792 desc.m_Eps = param.eps();
1793
1794 unsigned int channels = inputInfo.GetShape()[1];
1795 unsigned int shape[] = {channels};
1796
1797 vector<float> meanData(channels);
1798 GetDataFromBlob(layerParam, meanData, 0);
1799
1800 vector<float> varianceData(channels);
1801 GetDataFromBlob(layerParam, varianceData, 1);
1802
telsoa01c577f2c2018-08-31 09:22:23 +01001803 // Reads moving average factor and applies scaling (if required).
Matthew Sloyan589e3e82020-09-11 16:17:48 +01001804 const BlobProto& blob = layerParam.blobs(armnn::numeric_cast<int>(2));
1805 const float movingAverageFactor = blob.data(armnn::numeric_cast<int>(0));
surmeh013537c2c2018-05-18 16:31:43 +01001806 if(movingAverageFactor != 0.0f)
1807 {
1808 const float scaleFactor = 1.0f / movingAverageFactor;
1809 auto scaleFunction = [scaleFactor](float f) -> float { return f * scaleFactor; };
1810
1811 std::transform(varianceData.begin(), varianceData.end(), varianceData.begin(), scaleFunction);
1812 std::transform(meanData.begin(), meanData.end(), meanData.begin(), scaleFunction);
1813 }
1814
telsoa01c577f2c2018-08-31 09:22:23 +01001815 // Identifies scale operation.
telsoa014fcda012018-03-09 14:13:49 +00001816 vector<float> betaData(channels, 0.0f);
1817 vector<float> gammaData(channels, 1.0f);
1818
1819 ConstTensor mean(TensorInfo(1, shape, armnn::DataType::Float32), meanData);
1820 ConstTensor variance(TensorInfo(1, shape, armnn::DataType::Float32), varianceData);
1821 ConstTensor beta(TensorInfo(1, shape, armnn::DataType::Float32), betaData);
1822 ConstTensor gamma(TensorInfo(1, shape, armnn::DataType::Float32), gammaData);
1823
1824 armnn::IConnectableLayer* const batchNormLayer = m_Network->AddBatchNormalizationLayer(desc,
1825 mean, variance, beta, gamma, name.c_str());
1826 GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).Connect(batchNormLayer->GetInputSlot(0));
1827 batchNormLayer->GetOutputSlot(0).SetTensorInfo(inputInfo);
1828 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), batchNormLayer->GetOutputSlot(0));
1829}
1830
Kevin Mayef33cb12021-01-29 14:24:57 +00001831void ICaffeParser::CaffeParserImpl::ParseScaleLayer(const LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +00001832{
telsoa01c577f2c2018-08-31 09:22:23 +01001833 // Current unoptimal solution: add a batchnormalization layer with 0 mean and 1 variance.
telsoa014fcda012018-03-09 14:13:49 +00001834 ValidateNumInputsOutputs(layerParam, 1, 1);
1835
1836 const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo();
1837
1838 string name = layerParam.name();
1839
1840 ScaleParameter param = layerParam.scale_param();
1841 if (param.axis() != 1)
1842 {
1843 // Would have to use something other than BatchNormalizationLayer in this case
telsoa01c577f2c2018-08-31 09:22:23 +01001844 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001845 fmt::format("Loading Scale Layer: Only axis 1 is supported currently. "
1846 "Layer={} Axis={} {}",
1847 layerParam.name(),
1848 param.axis(),
1849 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001850 }
1851
1852 unsigned int channels = inputInfo.GetShape()[1];
1853 unsigned int shape[] = {channels};
1854
1855 BatchNormalizationDescriptor desc;
telsoa01c577f2c2018-08-31 09:22:23 +01001856 desc.m_Eps = 0.0f; // Don't need epsilon if variance is 1.
telsoa014fcda012018-03-09 14:13:49 +00001857 vector<float> meanData(channels, 0.0f);
1858 vector<float> varianceData(channels, 1.0f);
1859 vector<float> betaData(channels, 0.0f);
1860 vector<float> gammaData(channels);
1861
1862 GetDataFromBlob(layerParam, gammaData, 0);
1863
1864 if(param.has_bias_term())
1865 {
1866 GetDataFromBlob(layerParam, betaData, 1);
1867 }
1868
1869 ConstTensor mean(TensorInfo(1, shape, armnn::DataType::Float32), meanData);
1870 ConstTensor variance(TensorInfo(1, shape, armnn::DataType::Float32), varianceData);
1871 ConstTensor beta(TensorInfo(1, shape, armnn::DataType::Float32), betaData);
1872 ConstTensor gamma(TensorInfo(1, shape, armnn::DataType::Float32), gammaData);
1873
1874 armnn::IConnectableLayer* const batchNormLayer = m_Network->AddBatchNormalizationLayer(desc,
1875 mean, variance, beta, gamma, name.c_str());
1876 GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).Connect(batchNormLayer->GetInputSlot(0));
1877 batchNormLayer->GetOutputSlot(0).SetTensorInfo(inputInfo);
1878 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), batchNormLayer->GetOutputSlot(0));
1879}
1880
Kevin Mayef33cb12021-01-29 14:24:57 +00001881void ICaffeParser::CaffeParserImpl::ParseSplitLayer(const caffe::LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +00001882{
telsoa01c577f2c2018-08-31 09:22:23 +01001883 // Used in caffe to duplicate memory - not necessary in armnn.
telsoa014fcda012018-03-09 14:13:49 +00001884 if (layerParam.bottom_size() != 1)
1885 {
telsoa01c577f2c2018-08-31 09:22:23 +01001886 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001887 fmt::format("Split layer '{}' should have exactly 1 bottom. "
1888 "#bottoms={} {}",
1889 layerParam.name(),
1890 layerParam.bottom_size(),
1891 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001892 }
1893 armnn::IOutputSlot& outputSlot = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0));
1894 for (int i = 0; i < layerParam.top_size(); i++)
1895 {
1896 SetArmnnOutputSlotForCaffeTop(layerParam.top(i), outputSlot);
1897 }
1898}
1899
Kevin Mayef33cb12021-01-29 14:24:57 +00001900void ICaffeParser::CaffeParserImpl::ParseDropoutLayer(const caffe::LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +00001901{
telsoa01c577f2c2018-08-31 09:22:23 +01001902 // Ignored for inference, so patch the single input to its single output.
telsoa014fcda012018-03-09 14:13:49 +00001903 if (layerParam.bottom_size() != 1 || layerParam.top_size() != 1)
1904 {
telsoa01c577f2c2018-08-31 09:22:23 +01001905 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001906 fmt::format("Dropout layer '{}' should have exactly 1 bottom and 1 top. "
1907 "#bottoms={} #tops={} {}",
1908 layerParam.name(),
1909 layerParam.bottom_size(),
1910 layerParam.top_size(),
1911 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001912 }
1913 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)));
1914}
1915
Kevin Mayef33cb12021-01-29 14:24:57 +00001916void ICaffeParser::CaffeParserImpl::TrackInputBinding(armnn::IConnectableLayer* layer,
telsoa014fcda012018-03-09 14:13:49 +00001917 armnn::LayerBindingId id,
1918 const armnn::TensorInfo& tensorInfo)
1919{
1920 return TrackBindingPoint(layer, id, tensorInfo, layer->GetName(), m_NetworkInputsBindingInfo);
1921}
1922
Kevin Mayef33cb12021-01-29 14:24:57 +00001923void ICaffeParser::CaffeParserImpl::TrackOutputBinding(armnn::IConnectableLayer* layer,
telsoa014fcda012018-03-09 14:13:49 +00001924 armnn::LayerBindingId id,
1925 const armnn::TensorInfo& tensorInfo)
1926{
1927 return TrackBindingPoint(layer, id, tensorInfo, layer->GetName(), m_NetworkOutputsBindingInfo);
1928}
1929
Kevin Mayef33cb12021-01-29 14:24:57 +00001930void ICaffeParser::CaffeParserImpl::TrackBindingPoint(armnn::IConnectableLayer* layer,
telsoa014fcda012018-03-09 14:13:49 +00001931 armnn::LayerBindingId id,
1932 const armnn::TensorInfo& tensorInfo,
1933 const char* bindingPointDesc,
1934 std::unordered_map<std::string, BindingPointInfo>& nameToBindingInfo)
1935{
1936 const std::string layerName = layer->GetName();
1937 auto it = nameToBindingInfo.find(layerName);
1938 if (it == nameToBindingInfo.end())
1939 {
1940 nameToBindingInfo[layerName] = std::make_pair(id, tensorInfo);
1941 }
1942 else
1943 {
telsoa01c577f2c2018-08-31 09:22:23 +01001944 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001945 fmt::format("Id {} used by more than one {} layer {}",
1946 id,
1947 bindingPointDesc,
1948 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001949 }
1950}
1951
Kevin Mayef33cb12021-01-29 14:24:57 +00001952armnn::IOutputSlot& ICaffeParser::CaffeParserImpl::GetArmnnOutputSlotForCaffeTop(const std::string& caffeTopName) const
telsoa014fcda012018-03-09 14:13:49 +00001953{
1954 auto it = m_ArmnnOutputSlotForCaffeTop.find(caffeTopName);
1955 if (it != m_ArmnnOutputSlotForCaffeTop.end())
1956 {
1957 return *it->second;
1958 }
1959 else
1960 {
telsoa01c577f2c2018-08-31 09:22:23 +01001961 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001962 fmt::format("Could not find armnn output slot for Caffe top '{}' {}",
1963 caffeTopName,
1964 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001965 }
1966}
1967
Kevin Mayef33cb12021-01-29 14:24:57 +00001968void ICaffeParser::CaffeParserImpl::SetArmnnOutputSlotForCaffeTop(
telsoa01c577f2c2018-08-31 09:22:23 +01001969 const std::string& caffeTopName, armnn::IOutputSlot& armnnOutputSlot)
telsoa014fcda012018-03-09 14:13:49 +00001970{
1971 auto it = m_ArmnnOutputSlotForCaffeTop.find(caffeTopName);
1972 if (it == m_ArmnnOutputSlotForCaffeTop.end())
1973 {
1974 m_ArmnnOutputSlotForCaffeTop[caffeTopName] = &armnnOutputSlot;
1975 }
1976 else
1977 {
telsoa01c577f2c2018-08-31 09:22:23 +01001978 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001979 fmt::format("Attempting to add duplicate entry for Caffe top '{}' {}",
1980 caffeTopName,
1981 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001982 }
1983}
1984
telsoa01c577f2c2018-08-31 09:22:23 +01001985// Note: can move to CaffeParser when/if we optimise the text/string format
1986// to load on a layer by layer basis
Kevin Mayef33cb12021-01-29 14:24:57 +00001987void ICaffeParser::CaffeParserImpl::ResolveInPlaceLayers(caffe::NetParameter& netParameter)
telsoa014fcda012018-03-09 14:13:49 +00001988{
telsoa01c577f2c2018-08-31 09:22:23 +01001989 // Finds layers with the same top.
telsoa014fcda012018-03-09 14:13:49 +00001990 std::map<std::string, std::vector<caffe::LayerParameter*>> layersByTop;
1991 for (int layerIdx = 0; layerIdx < netParameter.layer_size(); ++layerIdx)
1992 {
1993 caffe::LayerParameter& layer = *netParameter.mutable_layer(layerIdx);
telsoa01c577f2c2018-08-31 09:22:23 +01001994 std::string name = layer.name();
telsoa014fcda012018-03-09 14:13:49 +00001995 for (int i = 0; i < layer.top_size(); ++i)
1996 {
1997 layersByTop[layer.top(i)].push_back(&layer);
1998 }
1999 }
2000
telsoa01c577f2c2018-08-31 09:22:23 +01002001 // 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 +00002002 // Note that for 'regular' layers, there will be a single layer in each group and so this will be a no-op.
2003 for (auto layersWithSameTopIt : layersByTop)
2004 {
2005 const std::string& top = layersWithSameTopIt.first;
2006 const std::vector<caffe::LayerParameter*>& layersWithSameTop = layersWithSameTopIt.second;
2007
telsoa01c577f2c2018-08-31 09:22:23 +01002008 // Chains the layers together in the order that they are listed in the prototxt (hopefully this is correct).
telsoa014fcda012018-03-09 14:13:49 +00002009 // Note that the last layer will not have its top modified so that other layers will continue to reference it.
2010 for (unsigned int layerIdx = 0; layerIdx < layersWithSameTop.size() - 1; ++layerIdx)
2011 {
2012 caffe::LayerParameter& layer1 = *layersWithSameTop[layerIdx];
2013 caffe::LayerParameter& layer2 = *layersWithSameTop[layerIdx+1];
2014 if (layer1.top_size() != 1)
2015 {
telsoa01c577f2c2018-08-31 09:22:23 +01002016 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002017 fmt::format("Node '{}' is an in-place layer but doesn't have exactly one "
2018 "top. It has {} instead. {}",
2019 layer1.name(),
2020 layer1.top_size(),
2021 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00002022 }
2023 std::string newTop = layer1.name() + "_top";
2024 layer1.set_top(0, newTop);
2025 if (layer2.bottom_size() != 1 || layer2.bottom(0) != top)
2026 {
telsoa01c577f2c2018-08-31 09:22:23 +01002027 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002028 fmt::format("Node '{}' is an in-place layer but "
2029 "doesn't have exactly one bottom, or it doesn't match its top. "
2030 "#bottoms={}, first bottom is {}, top is {} {}",
2031 layer2.name(),
2032 layer2.bottom(0),
2033 top,
2034 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00002035 }
2036 layer2.set_bottom(0, newTop);
2037 }
2038 }
2039}
2040
telsoa01c577f2c2018-08-31 09:22:23 +01002041// Note: can move to CaffeParser when/if we optimise the text/string format
2042// to load on a layer by layer basis
Kevin Mayef33cb12021-01-29 14:24:57 +00002043void ICaffeParser::CaffeParserImpl::LoadNetParam(NetParameter& netParameter)
telsoa014fcda012018-03-09 14:13:49 +00002044{
telsoa01c577f2c2018-08-31 09:22:23 +01002045 // Caffe models sometimes have an implicit input layer.
2046 // In that case, add an explicit one.
telsoa014fcda012018-03-09 14:13:49 +00002047 if (netParameter.input_size() > 0)
2048 {
2049 LayerParameter* newLayer = netParameter.add_layer();
2050
2051 newLayer->set_type("Input");
2052 newLayer->set_name(netParameter.input(0));
2053 newLayer->add_top(netParameter.input(0));
2054
2055 InputParameter* inputParam = newLayer->mutable_input_param();
2056 BlobShape* shape = inputParam->add_shape();
2057
2058 int dim_size = netParameter.input_dim_size();
2059 for (int i = 0; i < dim_size; ++i)
2060 {
2061 shape->add_dim(netParameter.input_dim(i));
2062 }
2063 }
2064
telsoa01c577f2c2018-08-31 09:22:23 +01002065 // Replaces in-place layers with regular ones to make the rest of the parsing easier.
telsoa014fcda012018-03-09 14:13:49 +00002066 ResolveInPlaceLayers(netParameter);
2067
telsoa01c577f2c2018-08-31 09:22:23 +01002068 // Creates a lookup of Caffe layers by name.
telsoa014fcda012018-03-09 14:13:49 +00002069 for (int i = 0; i < netParameter.layer_size(); ++i)
2070 {
2071 const caffe::LayerParameter& layer = netParameter.layer(i);
2072 for (int i = 0; i < layer.top_size(); ++i)
2073 {
2074 m_CaffeLayersByTopName[layer.top(i)] = &layer;
2075 }
2076 }
2077
telsoa01c577f2c2018-08-31 09:22:23 +01002078 // Finds the output layers the user requested.
telsoa014fcda012018-03-09 14:13:49 +00002079 std::vector<const caffe::LayerParameter*> targetLayers;
2080 for (const std::string& requestedOutputName : m_RequestedOutputs)
2081 {
2082 auto nodeIt = m_CaffeLayersByTopName.find(requestedOutputName);
2083 if (nodeIt == m_CaffeLayersByTopName.end())
2084 {
telsoa01c577f2c2018-08-31 09:22:23 +01002085 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002086 fmt::format("Couldn't find requested output layer '{}' in graph {}",
2087 requestedOutputName,
2088 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00002089 }
2090 targetLayers.push_back(nodeIt->second);
2091 }
2092
telsoa01c577f2c2018-08-31 09:22:23 +01002093 // Sorts them into a linear ordering such that all inputs of a node are before the node itself.
telsoa014fcda012018-03-09 14:13:49 +00002094 std::vector<const caffe::LayerParameter*> sortedNodes;
2095 if (!armnnUtils::GraphTopologicalSort<const caffe::LayerParameter*>(
2096 targetLayers,
2097 [this](const caffe::LayerParameter* node)
2098 {
2099 return GetInputs(*node);
2100 },
2101 sortedNodes))
2102 {
telsoa01c577f2c2018-08-31 09:22:23 +01002103 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002104 fmt::format("Cycle detected in graph. #nodes: {} {}",
2105 sortedNodes.size(),
2106 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00002107 }
2108
telsoa01c577f2c2018-08-31 09:22:23 +01002109 // 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 +00002110 for (const caffe::LayerParameter* current : sortedNodes)
2111 {
2112 auto it = ms_CaffeLayerNameToParsingFunctions.find(current->type());
2113 if (it == ms_CaffeLayerNameToParsingFunctions.end())
2114 {
telsoa01c577f2c2018-08-31 09:22:23 +01002115 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002116 fmt::format("Unsupported layer type: '{}' for layer {} {}",
2117 current->type(),
2118 current->name(),
2119 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00002120 }
2121 auto func = it->second;
2122 (this->*func)(*current);
2123 }
2124
telsoa01c577f2c2018-08-31 09:22:23 +01002125 // Adds ArmNN output layers connected to each requested output.
telsoa014fcda012018-03-09 14:13:49 +00002126 for (const std::string& requestedOutput : m_RequestedOutputs)
2127 {
2128 armnn::IOutputSlot& outputSlot = GetArmnnOutputSlotForCaffeTop(requestedOutput);
2129
Matthew Sloyan589e3e82020-09-11 16:17:48 +01002130 const armnn::LayerBindingId outputId = armnn::numeric_cast<armnn::LayerBindingId>(
telsoa014fcda012018-03-09 14:13:49 +00002131 m_NetworkOutputsBindingInfo.size());
2132 armnn::IConnectableLayer* const outputLayer = m_Network->AddOutputLayer(outputId, requestedOutput.c_str());
2133 outputSlot.Connect(outputLayer->GetInputSlot(0));
2134
2135 TrackOutputBinding(outputLayer, outputId, outputLayer->GetInputSlot(0).GetConnection()->GetTensorInfo());
2136 }
2137}
2138
Kevin Mayef33cb12021-01-29 14:24:57 +00002139INetworkPtr ICaffeParser::CaffeParserImpl::CreateNetworkFromTextFile(const char* graphFile,
telsoa014fcda012018-03-09 14:13:49 +00002140 const std::map<std::string, armnn::TensorShape>& inputShapes,
2141 const std::vector<std::string>& requestedOutputs)
2142{
2143 FILE* fd = fopen(graphFile, "r");
2144
2145 if (fd == nullptr)
2146 {
telsoa01c577f2c2018-08-31 09:22:23 +01002147 throw FileNotFoundException(
James Ward58dec6b2020-09-11 17:32:44 +01002148 fmt::format("Failed to open graph file: {} {}",
2149 graphFile,
2150 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00002151 }
2152
telsoa01c577f2c2018-08-31 09:22:23 +01002153 // Parses the file into a message.
telsoa014fcda012018-03-09 14:13:49 +00002154 NetParameter netParam;
2155 auto input = new google::protobuf::io::FileInputStream(fileno(fd));
2156 bool success = google::protobuf::TextFormat::Parse(input, &netParam);
2157 delete input;
2158 fclose(fd);
2159
2160 if (!success)
2161 {
telsoa01c577f2c2018-08-31 09:22:23 +01002162 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002163 fmt::format("Failed to parse graph file: {} {}",
2164 graphFile,
2165 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00002166 }
2167
2168 return CreateNetworkFromNetParameter(netParam, inputShapes, requestedOutputs);
2169}
2170
Kevin Mayef33cb12021-01-29 14:24:57 +00002171INetworkPtr ICaffeParser::CaffeParserImpl::CreateNetworkFromString(const char* protoText,
telsoa014fcda012018-03-09 14:13:49 +00002172 const std::map<std::string, armnn::TensorShape>& inputShapes,
2173 const std::vector<std::string>& requestedOutputs)
2174{
telsoa01c577f2c2018-08-31 09:22:23 +01002175 // Parses the string into a message.
telsoa014fcda012018-03-09 14:13:49 +00002176 NetParameter netParam;
2177 bool success = google::protobuf::TextFormat::ParseFromString(protoText, &netParam);
2178
2179 if (!success)
2180 {
telsoa01c577f2c2018-08-31 09:22:23 +01002181 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002182 fmt::format("Failed to parse graph string {}",
2183 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00002184 }
2185
2186 return CreateNetworkFromNetParameter(netParam, inputShapes, requestedOutputs);
2187}
2188
2189INetworkPtr CaffeParser::CreateNetworkFromBinaryFile(const char* graphFile,
2190 const std::map<std::string, armnn::TensorShape>& inputShapes,
2191 const std::vector<std::string>& requestedOutputs)
2192{
2193 FILE* fd = fopen(graphFile, "rb");
2194
2195 if (fd == nullptr)
2196 {
telsoa01c577f2c2018-08-31 09:22:23 +01002197 throw FileNotFoundException(
James Ward58dec6b2020-09-11 17:32:44 +01002198 fmt::format("Failed to open graph file at: {} {}",
2199 graphFile,
2200 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00002201 }
2202
telsoa01c577f2c2018-08-31 09:22:23 +01002203 // Parses the file into a message.
telsoa014fcda012018-03-09 14:13:49 +00002204 NetParameter netParam;
2205
2206 FileInputStream inStream(fileno(fd));
2207 CodedInputStream codedStream(&inStream);
Nikhil Raje5181532020-10-09 14:52:25 +01002208 codedStream.SetTotalBytesLimit(INT_MAX);
telsoa014fcda012018-03-09 14:13:49 +00002209 bool success = netParam.ParseFromCodedStream(&codedStream);
2210 fclose(fd);
2211
2212 if (!success)
2213 {
telsoa01c577f2c2018-08-31 09:22:23 +01002214 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002215 fmt::format("Failed to parse protobuf file: {} {}",
2216 graphFile,
2217 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00002218 }
2219
2220 return CreateNetworkFromNetParameter(netParam, inputShapes, requestedOutputs);
2221}
2222
telsoa01c577f2c2018-08-31 09:22:23 +01002223// Note: can move to CaffeParser when/if we optimise the text/string format
2224// to load on a layer by layer basis
Kevin Mayef33cb12021-01-29 14:24:57 +00002225INetworkPtr ICaffeParser::CaffeParserImpl::CreateNetworkFromNetParameter(NetParameter& netParam,
telsoa014fcda012018-03-09 14:13:49 +00002226 const std::map<std::string, armnn::TensorShape>& inputShapes,
2227 const std::vector<std::string>& requestedOutputs)
2228{
2229 m_NetworkInputsBindingInfo.clear();
2230 m_NetworkOutputsBindingInfo.clear();
2231
2232 m_Network = INetwork::Create();
2233
2234 m_InputShapes = inputShapes;
2235 if (requestedOutputs.size() == 0)
2236 {
2237 throw ParseException("requestedOutputs must have at least one entry");
2238 }
2239 m_RequestedOutputs = requestedOutputs;
2240
2241 try
2242 {
2243 LoadNetParam(netParam);
2244 }
2245 catch (const ParseException& e)
2246 {
2247 Cleanup();
2248 throw e;
2249 }
2250
2251 Cleanup();
2252
2253 return move(m_Network);
2254}
2255
Matthew Sloyanac001ee2021-02-03 10:43:04 +00002256const std::string ICaffeParser::CaffeParserImpl::GetVersion()
2257{
2258 return CAFFE_PARSER_VERSION;
2259}
2260
Kevin Mayef33cb12021-01-29 14:24:57 +00002261void ICaffeParser::CaffeParserImpl::Cleanup() {
telsoa014fcda012018-03-09 14:13:49 +00002262 // cleanup, in case we reuse this parser
telsoa014fcda012018-03-09 14:13:49 +00002263 m_InputShapes.clear();
2264 m_RequestedOutputs.clear();
2265 m_ArmnnOutputSlotForCaffeTop.clear();
telsoa01c577f2c2018-08-31 09:22:23 +01002266 // NOTE: when we get the text/string format
2267 // optimised for memory then this data structure can
2268 // also move to the CaffeParser class
2269 m_CaffeLayersByTopName.clear();
telsoa014fcda012018-03-09 14:13:49 +00002270}
2271
2272}