blob: dfb9cec206f81d298dd24574347d4bc0ea5700a6 [file] [log] [blame]
Laurent Carlier749294b2020-06-01 09:03:17 +01001//
telsoa014fcda012018-03-09 14:13:49 +00002// Copyright © 2017 Arm Ltd. All rights reserved.
David Beckecb56cd2018-09-05 12:52:57 +01003// SPDX-License-Identifier: MIT
telsoa014fcda012018-03-09 14:13:49 +00004//
5#include "CaffeParser.hpp"
telsoa01c577f2c2018-08-31 09:22:23 +01006#include "RecordByRecordCaffeParser.hpp"
telsoa014fcda012018-03-09 14:13:49 +00007
8#include "armnn/Descriptors.hpp"
9#include "armnn/INetwork.hpp"
10#include "armnn/Utils.hpp"
11#include "armnn/Exceptions.hpp"
12
13#include "GraphTopologicalSort.hpp"
telsoa01c577f2c2018-08-31 09:22:23 +010014#include "VerificationHelpers.hpp"
telsoa014fcda012018-03-09 14:13:49 +000015
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +010016#include <armnn/utility/Assert.hpp>
Matthew Sloyan589e3e82020-09-11 16:17:48 +010017#include <armnn/utility/NumericCast.hpp>
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +010018
James Ward58dec6b2020-09-11 17:32:44 +010019#include <fmt/format.h>
telsoa014fcda012018-03-09 14:13:49 +000020
21// Caffe
22#include "caffe/proto/caffe.pb.h"
23
24// ProtoBuf
25#include <google/protobuf/io/coded_stream.h>
26#include <google/protobuf/io/zero_copy_stream.h>
27#include <google/protobuf/io/zero_copy_stream_impl.h>
28#include <google/protobuf/text_format.h>
29#include <google/protobuf/stubs/common.h>
30#include <google/protobuf/stubs/once.h>
31#include <google/protobuf/io/coded_stream.h>
telsoa014fcda012018-03-09 14:13:49 +000032#include <google/protobuf/descriptor.h>
33#include <google/protobuf/generated_message_reflection.h>
34#include <google/protobuf/reflection_ops.h>
35#include <google/protobuf/wire_format.h>
36
37#include <cmath>
38#include <sstream>
39#include <queue>
40#include <fcntl.h>
41
42/// Caffe networks are loaded from protobuf files (binary or text) using the protobuf library and the generated
43/// code from caffe.pb.h. This gives us a caffe::NetParameter which is an in-memory version of the file.
44/// This contains a flat list of Caffe 'layers' (e.g. convolution, pooling etc.).
45/// Each layer has inputs (called "bottoms") and outputs (called "tops"). Data flows from bottom to top.
46/// The bottoms of a layer refer to the tops of other layers, not their names.
telsoa01c577f2c2018-08-31 09:22:23 +010047/// The names of layers seem to be arbitrary (you could rename a layer and the network wouldn't
48/// need any other changes).
telsoa014fcda012018-03-09 14:13:49 +000049///
50/// Some layers (e.g. Relu) can be configured so that their top and bottom are both the same. This is called an
51/// "in-place" layer and is a Caffe runtime feature used to reduce memory usage by modifying tensors in-place.
52/// This isn't relevant to the parser and so we preprocess these layers to convert them to regular layers, to result
53/// in a consistent graph structure.
54
55namespace armnnCaffeParser
56{
57
58using namespace armnn;
59using namespace caffe;
60using namespace std;
61using namespace google::protobuf::io;
62
Kevin Mayef33cb12021-01-29 14:24:57 +000063ICaffeParser::ICaffeParser() : pCaffeParserImpl(new RecordByRecordCaffeParser()) {}
64
65ICaffeParser::~ICaffeParser() = default;
66
67ICaffeParser* ICaffeParser::CreateRaw()
68{
69 return new ICaffeParser();
70}
71
72ICaffeParserPtr ICaffeParser::Create()
73{
74 return ICaffeParserPtr(CreateRaw(), &ICaffeParser::Destroy);
75}
76
77void ICaffeParser::Destroy(ICaffeParser* parser)
78{
79 delete parser;
80}
81
82armnn::INetworkPtr ICaffeParser::CreateNetworkFromTextFile(
83 const char* graphFile,
84 const std::map<std::string, armnn::TensorShape>& inputShapes,
85 const std::vector<std::string>& requestedOutputs)
86{
87 return pCaffeParserImpl->CreateNetworkFromTextFile(graphFile, inputShapes, requestedOutputs);
88}
89
90armnn::INetworkPtr ICaffeParser::CreateNetworkFromBinaryFile(
91 const char* graphFile,
92 const std::map<std::string, armnn::TensorShape>& inputShapes,
93 const std::vector<std::string>& requestedOutputs)
94{
95 return pCaffeParserImpl->CreateNetworkFromBinaryFile(graphFile, inputShapes,requestedOutputs);
96}
97
98armnn::INetworkPtr ICaffeParser::CreateNetworkFromString(
99 const char* protoText,
100 const std::map<std::string, armnn::TensorShape>& inputShapes,
101 const std::vector<std::string>& requestedOutputs)
102{
103 return pCaffeParserImpl->CreateNetworkFromString(protoText, inputShapes, requestedOutputs);
104}
105
106BindingPointInfo ICaffeParser::GetNetworkInputBindingInfo(const std::string& name) const
107{
108 return pCaffeParserImpl->GetNetworkInputBindingInfo(name);
109}
110
111BindingPointInfo ICaffeParser::GetNetworkOutputBindingInfo(const std::string& name) const
112{
113 return pCaffeParserImpl->GetNetworkOutputBindingInfo(name);
114}
115
telsoa01c577f2c2018-08-31 09:22:23 +0100116namespace
telsoa014fcda012018-03-09 14:13:49 +0000117{
118
telsoa01c577f2c2018-08-31 09:22:23 +0100119const float* GetArrayPtrFromBlob(const LayerParameter& layerParam, unsigned int blobIndex)
telsoa014fcda012018-03-09 14:13:49 +0000120{
telsoa01c577f2c2018-08-31 09:22:23 +0100121 auto nBlobs = layerParam.blobs_size();
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100122 if (blobIndex >= armnn::numeric_cast<unsigned int>(nBlobs))
telsoa014fcda012018-03-09 14:13:49 +0000123 {
telsoa01c577f2c2018-08-31 09:22:23 +0100124 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100125 fmt::format("Expected data blob at index {} in layer {} not found. nBlobs={}. {}",
126 blobIndex,
127 layerParam.name(),
128 nBlobs,
129 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +0000130 }
131
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100132 const BlobProto& blob = layerParam.blobs(armnn::numeric_cast<int>(blobIndex));
telsoa014fcda012018-03-09 14:13:49 +0000133
telsoa01c577f2c2018-08-31 09:22:23 +0100134 const float* arrayPtr = blob.data().data();
135 return arrayPtr;
136}
137
138void GetDataFromBlob(const LayerParameter& layerParam, vector<float>& outData, unsigned int blobIndex)
139{
140 auto nBlobs = layerParam.blobs_size();
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100141 if (blobIndex >= armnn::numeric_cast<unsigned int>(nBlobs))
telsoa014fcda012018-03-09 14:13:49 +0000142 {
telsoa01c577f2c2018-08-31 09:22:23 +0100143 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100144 fmt::format("Expected data blob at index {} in layer {} not found. {}",
145 blobIndex,
146 layerParam.name(),
147 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +0000148 }
149
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100150 const BlobProto& blob = layerParam.blobs(armnn::numeric_cast<int>(blobIndex));
telsoa01c577f2c2018-08-31 09:22:23 +0100151
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100152 size_t blobSize = armnn::numeric_cast<size_t>(blob.data_size());
telsoa01c577f2c2018-08-31 09:22:23 +0100153 if (blobSize != outData.size())
telsoa014fcda012018-03-09 14:13:49 +0000154 {
telsoa01c577f2c2018-08-31 09:22:23 +0100155 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100156 fmt::format("Data blob at index {} in layer {} has an unexpected size. "
157 "Expected {} elements but got {} elements. {}",
158 blobIndex,
159 layerParam.name(),
160 outData.size(),
161 blobSize,
162 CHECK_LOCATION().AsString()));
telsoa01c577f2c2018-08-31 09:22:23 +0100163 }
164
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100165 int outSizeInt = armnn::numeric_cast<int>(outData.size());
telsoa01c577f2c2018-08-31 09:22:23 +0100166 for (int i = 0; i < outSizeInt; ++i)
167 {
168 outData[static_cast<size_t>(i)] = blob.data(i);
telsoa014fcda012018-03-09 14:13:49 +0000169 }
170}
171
telsoa014fcda012018-03-09 14:13:49 +0000172template <typename T>
173size_t SizeOfVectorData(const vector<T>& vec)
174{
175 return vec.size() * sizeof(T);
176}
177
178void ValidateNumInputsOutputs(const caffe::LayerParameter& layerParameter,
179 unsigned int numInputs,
180 unsigned int numOutputs)
181{
182 int numInputsActual = layerParameter.bottom_size();
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100183 if (numInputs != armnn::numeric_cast<unsigned int>(numInputsActual))
telsoa014fcda012018-03-09 14:13:49 +0000184 {
telsoa01c577f2c2018-08-31 09:22:23 +0100185 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100186 fmt::format("Invalid number of inputs requested {} for layer {} "
187 "while only {} present. {}",
188 numInputs,
189 layerParameter.name(),
190 numInputsActual,
191 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +0000192 }
193
194 int numOutputsActual = layerParameter.top_size();
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100195 if (numOutputs != armnn::numeric_cast<unsigned int>(numOutputsActual))
telsoa014fcda012018-03-09 14:13:49 +0000196 {
telsoa01c577f2c2018-08-31 09:22:23 +0100197 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100198 fmt::format("Invalid number of outputs requested {} for layer {} "
199 "while only {} present. {}",
200 numOutputs,
201 layerParameter.name(),
202 numOutputsActual,
203 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +0000204 }
205}
206
telsoa01c577f2c2018-08-31 09:22:23 +0100207template <typename ParamType, typename ExtractOptional, typename ExtractFallback, typename ValueType>
208ValueType GetOptionalWithFallback(const ParamType& param,
209 ExtractOptional extractOptional,
210 ExtractFallback extractFallback,
211 ValueType defaultValue)
212{
213 auto optValue = extractOptional(param, defaultValue);
214 if (optValue.first)
215 {
216 return optValue.second;
217 }
218 auto fallbackValue = extractFallback(param, defaultValue);
219 return fallbackValue.second;
220}
221
222#define GET_OPTIONAL_WITH_VECTOR_FALLBACK(PARAM, \
223 PARAM_TYPE, \
224 OPTIONAL_VALUE, \
225 FALLBACK_VECTOR, \
226 VALUE_TYPE, \
227 DEFAULT_VALUE) \
228 GetOptionalWithFallback( \
229 PARAM, \
230 [](const PARAM_TYPE & param, VALUE_TYPE defaultValue) \
231 { \
232 if (param.has_##OPTIONAL_VALUE ()) \
233 { \
234 return std::make_pair(true, param.OPTIONAL_VALUE ()); \
235 } \
236 else \
237 { \
238 return std::make_pair(false, defaultValue); \
239 } \
240 }, \
241 [](const PARAM_TYPE & param, VALUE_TYPE defaultValue) \
242 { \
243 if (param.FALLBACK_VECTOR##_size() > 0) \
244 { \
245 return std::make_pair(true, (param.FALLBACK_VECTOR ()).Get(0)); \
246 } \
247 else \
248 { \
249 return std::make_pair(false, defaultValue); \
250 } \
251 }, \
252 DEFAULT_VALUE)
253
254#define GET_OPTIONAL_WITH_FALLBACK(PARAM, \
255 PARAM_TYPE, \
256 OPTIONAL_VALUE, \
257 FALLBACK_VALUE, \
258 VALUE_TYPE, \
259 DEFAULT_VALUE) \
260 GetOptionalWithFallback( \
261 PARAM, \
262 [](const PARAM_TYPE & param, VALUE_TYPE defaultValue) \
263 { \
264 if (param.has_##OPTIONAL_VALUE ()) \
265 { \
266 return std::make_pair(true, param.OPTIONAL_VALUE ()); \
267 } \
268 else \
269 { \
270 return std::make_pair(false, defaultValue); \
271 } \
272 }, \
273 [](const PARAM_TYPE & param, VALUE_TYPE defaultValue) \
274 { \
275 if (param.has_##FALLBACK_VALUE ()) \
276 { \
277 return std::make_pair(true, param.FALLBACK_VALUE ()); \
278 } \
279 else \
280 { \
281 return std::make_pair(false, defaultValue); \
282 } \
283 }, \
284 DEFAULT_VALUE)
285
telsoa01c577f2c2018-08-31 09:22:23 +0100286} // namespace <anonymous>
287
Kevin Mayef33cb12021-01-29 14:24:57 +0000288const std::map<std::string, ICaffeParser::CaffeParserImpl::OperationParsingFunction>
289 ICaffeParser::CaffeParserImpl::ms_CaffeLayerNameToParsingFunctions = {
290 { "Input", &CaffeParserImpl::ParseInputLayer },
291 { "Convolution", &CaffeParserImpl::ParseConvLayer },
292 { "Deconvolution",&CaffeParserImpl::ParseDeconvLayer },
293 { "Pooling", &CaffeParserImpl::ParsePoolingLayer },
294 { "ReLU", &CaffeParserImpl::ParseReluLayer },
295 { "LRN", &CaffeParserImpl::ParseLRNLayer },
296 { "InnerProduct", &CaffeParserImpl::ParseInnerProductLayer },
297 { "Softmax", &CaffeParserImpl::ParseSoftmaxLayer },
298 { "Eltwise", &CaffeParserImpl::ParseEltwiseLayer },
299 { "Concat", &CaffeParserImpl::ParseConcatLayer },
300 { "BatchNorm", &CaffeParserImpl::ParseBatchNormLayer },
301 { "Scale", &CaffeParserImpl::ParseScaleLayer },
302 { "Split", &CaffeParserImpl::ParseSplitLayer },
303 { "Dropout", &CaffeParserImpl::ParseDropoutLayer},
304 { "ArgMax", &CaffeParserImpl::ParseArgmaxLayer},
telsoa01c577f2c2018-08-31 09:22:23 +0100305};
306
Kevin Mayef33cb12021-01-29 14:24:57 +0000307ICaffeParser::CaffeParserImpl::CaffeParserImpl()
telsoa01c577f2c2018-08-31 09:22:23 +0100308 : m_Network(nullptr, nullptr)
309{
310
311}
312
313CaffeParser::CaffeParser()
Kevin Mayef33cb12021-01-29 14:24:57 +0000314: CaffeParserImpl()
telsoa01c577f2c2018-08-31 09:22:23 +0100315{
316
317}
318
Kevin Mayef33cb12021-01-29 14:24:57 +0000319BindingPointInfo ICaffeParser::CaffeParserImpl::GetNetworkInputBindingInfo(const std::string& name) const
telsoa014fcda012018-03-09 14:13:49 +0000320{
321 return GetBindingInfo(name, "input", m_NetworkInputsBindingInfo);
322}
323
Kevin Mayef33cb12021-01-29 14:24:57 +0000324BindingPointInfo ICaffeParser::CaffeParserImpl::GetNetworkOutputBindingInfo(const std::string& name) const
telsoa014fcda012018-03-09 14:13:49 +0000325{
326 return GetBindingInfo(name, "output", m_NetworkOutputsBindingInfo);
327}
328
Kevin Mayef33cb12021-01-29 14:24:57 +0000329std::pair<armnn::LayerBindingId, armnn::TensorInfo> ICaffeParser::CaffeParserImpl::GetBindingInfo(
330 const std::string& layerName,
telsoa014fcda012018-03-09 14:13:49 +0000331 const char* bindingPointDesc,
332 const std::unordered_map<std::string, BindingPointInfo>& nameToBindingInfo)
333{
334 auto it = nameToBindingInfo.find(layerName);
335 if (it == nameToBindingInfo.end())
336 {
telsoa01c577f2c2018-08-31 09:22:23 +0100337 throw InvalidArgumentException(
James Ward58dec6b2020-09-11 17:32:44 +0100338 fmt::format("Unknown binding {} for layer '{}'. {}",
339 bindingPointDesc,
340 layerName,
341 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +0000342 }
343 return it->second;
344}
345
Kevin Mayef33cb12021-01-29 14:24:57 +0000346TensorInfo ICaffeParser::CaffeParserImpl::BlobShapeToTensorInfo(const caffe::BlobShape& blobShape) const
telsoa014fcda012018-03-09 14:13:49 +0000347{
348 std::vector<unsigned int> shape;
349 for (int j = 0; j < blobShape.dim_size(); ++j)
350 {
351 shape.push_back(static_cast<unsigned int>(blobShape.dim(j)));
352 }
353
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100354 return TensorInfo(armnn::numeric_cast<unsigned int>(shape.size()), shape.data(), DataType::Float32);
telsoa014fcda012018-03-09 14:13:49 +0000355}
356
357BlobShape TensorDescToBlobShape(const TensorInfo& desc)
358{
359 BlobShape ret;
360 for (unsigned int i = 0; i < desc.GetNumDimensions(); ++i)
361 {
362 ret.add_dim(i);
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100363 ret.set_dim(armnn::numeric_cast<int>(i), desc.GetShape()[i]);
telsoa014fcda012018-03-09 14:13:49 +0000364 }
365
366 return ret;
367}
368
telsoa01c577f2c2018-08-31 09:22:23 +0100369// Note: can move to CaffeParser when/if we optimise the text/string format
370// to load on a layer by layer basis
Kevin Mayef33cb12021-01-29 14:24:57 +0000371vector<const LayerParameter*> ICaffeParser::CaffeParserImpl::GetInputs(const LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +0000372{
373 std::vector<const caffe::LayerParameter*> ret;
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100374 ret.reserve(armnn::numeric_cast<size_t>(layerParam.bottom_size()));
telsoa014fcda012018-03-09 14:13:49 +0000375 for (int j = 0; j < layerParam.bottom_size(); ++j)
376 {
377 std::string inputName = layerParam.bottom(j);
378 auto inputIt = m_CaffeLayersByTopName.find(inputName);
379 if (inputIt == m_CaffeLayersByTopName.end())
380 {
381 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100382 fmt::format("Can't find Caffe layer with top called '{}', "
383 "which is listed as an input of '{}'. {}",
384 inputName,
385 layerParam.name(),
386 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +0000387 }
388 ret.push_back(inputIt->second);
389 }
390
391 return ret;
392}
393
Kevin Mayef33cb12021-01-29 14:24:57 +0000394void ICaffeParser::CaffeParserImpl::ParseInputLayer(const LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +0000395{
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100396 ARMNN_ASSERT(layerParam.type() == "Input");
telsoa014fcda012018-03-09 14:13:49 +0000397 ValidateNumInputsOutputs(layerParam, 0, 1);
398
399 const InputParameter& param = layerParam.input_param();
400
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100401 const armnn::LayerBindingId inputId = armnn::numeric_cast<armnn::LayerBindingId>(
telsoa01c577f2c2018-08-31 09:22:23 +0100402 m_NetworkInputsBindingInfo.size());
telsoa014fcda012018-03-09 14:13:49 +0000403 armnn::IConnectableLayer* const inputLayer = m_Network->AddInputLayer(inputId, layerParam.name().c_str());
404
telsoa01c577f2c2018-08-31 09:22:23 +0100405 // Decides the tensor info for this input. This can be specified in the Caffe network but can also
telsoa014fcda012018-03-09 14:13:49 +0000406 // be overriden by user input (m_inputShapes).
407 armnn::TensorInfo inputTensorInfo;
408
409 const BlobShape* originalShape = param.shape_size() > 0 && param.shape(0).dim_size() > 0 ?
410 &param.shape(0) : nullptr;
411 if (originalShape)
412 {
413 inputTensorInfo = BlobShapeToTensorInfo(*originalShape);
414 }
415
416 auto overrideIt = m_InputShapes.find(layerParam.name());
417 if (overrideIt != m_InputShapes.end())
418 {
419 const TensorShape& overrideShape = overrideIt->second;
420 if (originalShape &&
421 ( originalShape->dim(1) != overrideShape[1]
422 || originalShape->dim(2) != overrideShape[2]
423 || originalShape->dim(3) != overrideShape[3]))
424 {
telsoa01c577f2c2018-08-31 09:22:23 +0100425 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100426 fmt::format("Parsed input shape for '{}' is incompatible with the override provided. {}",
427 layerParam.name(),
428 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +0000429 }
430 inputTensorInfo.SetShape(overrideShape);
431 }
432 else if (!originalShape)
433 {
telsoa01c577f2c2018-08-31 09:22:23 +0100434 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100435 fmt::format("No input descriptor given for '{}' and no input shape found in caffe model. {}",
436 layerParam.name(),
437 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +0000438 }
telsoa014fcda012018-03-09 14:13:49 +0000439 TrackInputBinding(inputLayer, inputId, inputTensorInfo);
440 inputLayer->GetOutputSlot(0).SetTensorInfo(inputTensorInfo);
441 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), inputLayer->GetOutputSlot(0));
442}
443
Kevin Mayef33cb12021-01-29 14:24:57 +0000444void ICaffeParser::CaffeParserImpl::AddConvLayerWithSplits(const caffe::LayerParameter& layerParam,
445 const armnn::Convolution2dDescriptor& desc,
446 unsigned int kernelW,
447 unsigned int kernelH)
telsoa014fcda012018-03-09 14:13:49 +0000448{
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100449 ARMNN_ASSERT(layerParam.type() == "Convolution");
telsoa014fcda012018-03-09 14:13:49 +0000450 ValidateNumInputsOutputs(layerParam, 1, 1);
451
telsoa01c577f2c2018-08-31 09:22:23 +0100452 ConvolutionParameter convParam = layerParam.convolution_param();
telsoa014fcda012018-03-09 14:13:49 +0000453 BlobShape inputShape = TensorDescToBlobShape(GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo());
telsoa01c577f2c2018-08-31 09:22:23 +0100454 const unsigned int numGroups = convParam.has_group() ? convParam.group() : 1;
telsoa014fcda012018-03-09 14:13:49 +0000455
telsoa01c577f2c2018-08-31 09:22:23 +0100456 // asusme these were already verified by the caller ParseConvLayer() function
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100457 ARMNN_ASSERT(numGroups < inputShape.dim(1));
458 ARMNN_ASSERT(numGroups > 1);
telsoa014fcda012018-03-09 14:13:49 +0000459
460 // Handle grouping
telsoa014fcda012018-03-09 14:13:49 +0000461 armnn::IOutputSlot& inputConnection = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0));
462
463 vector<string> convLayerNames(numGroups);
464 vector<armnn::IConnectableLayer*> convLayers(numGroups);
465 convLayerNames[0] = layerParam.name();
466
telsoa01c577f2c2018-08-31 09:22:23 +0100467 // This convolution is to be applied to chunks of the input data so add a splitter layer
468
469 // Redirect the convolution input to the splitter
470 unsigned int splitterDimSizes[4] = {static_cast<unsigned int>(inputShape.dim(0)),
471 static_cast<unsigned int>(inputShape.dim(1)),
472 static_cast<unsigned int>(inputShape.dim(2)),
473 static_cast<unsigned int>(inputShape.dim(3))};
474
475 // Split dimension 1 of the splitter output shape and conv input shapes
476 // according to the number of groups
477
478 splitterDimSizes[1] /= numGroups;
479 inputShape.set_dim(1, splitterDimSizes[1]);
480
481 // This is used to describe how the input is to be split
482 ViewsDescriptor splitterDesc(numGroups);
483
484 // Create an output node for each group, giving each a unique name
485 for (unsigned int g = 0; g < numGroups; ++g)
telsoa014fcda012018-03-09 14:13:49 +0000486 {
telsoa01c577f2c2018-08-31 09:22:23 +0100487 // Work out the names of the splitter layers child convolutions
488 stringstream ss;
489 ss << layerParam.name() << "_" << g;
490 convLayerNames[g] = ss.str();
telsoa014fcda012018-03-09 14:13:49 +0000491
telsoa01c577f2c2018-08-31 09:22:23 +0100492 splitterDesc.SetViewOriginCoord(g, 1, splitterDimSizes[1] * g);
telsoa014fcda012018-03-09 14:13:49 +0000493
telsoa01c577f2c2018-08-31 09:22:23 +0100494 // Set the size of the views.
495 for (unsigned int dimIdx=0; dimIdx < 4; dimIdx++)
telsoa014fcda012018-03-09 14:13:49 +0000496 {
telsoa01c577f2c2018-08-31 09:22:23 +0100497 splitterDesc.SetViewSize(g, dimIdx, splitterDimSizes[dimIdx]);
telsoa014fcda012018-03-09 14:13:49 +0000498 }
499 }
500
telsoa01c577f2c2018-08-31 09:22:23 +0100501 const std::string splitterLayerName = std::string("splitter_") + layerParam.bottom(0);
502 armnn::IConnectableLayer* splitterLayer = m_Network->AddSplitterLayer(splitterDesc, splitterLayerName.c_str());
telsoa014fcda012018-03-09 14:13:49 +0000503
telsoa01c577f2c2018-08-31 09:22:23 +0100504 inputConnection.Connect(splitterLayer->GetInputSlot(0));
505 for (unsigned int i = 0; i < splitterLayer->GetNumOutputSlots(); i++)
506 {
507 splitterLayer->GetOutputSlot(i).SetTensorInfo(BlobShapeToTensorInfo(inputShape));
508 }
telsoa014fcda012018-03-09 14:13:49 +0000509
510 unsigned int numFilters = convParam.num_output();
511
telsoa01c577f2c2018-08-31 09:22:23 +0100512 // Populates convolution output tensor descriptor dimensions.
telsoa014fcda012018-03-09 14:13:49 +0000513 BlobShape outputShape;
514 outputShape.add_dim(0);
515 outputShape.set_dim(0, inputShape.dim(0));
516 outputShape.add_dim(1);
telsoa01c577f2c2018-08-31 09:22:23 +0100517 // Ensures that dimension 1 of the convolution output is split according to the number of groups.
telsoa014fcda012018-03-09 14:13:49 +0000518 outputShape.set_dim(1, numFilters / numGroups);
519 outputShape.add_dim(2);
520 outputShape.set_dim(
telsoa01c577f2c2018-08-31 09:22:23 +0100521 2, (static_cast<int>(
Keith Mok7dc18202020-12-20 13:45:51 -0800522 static_cast<float>(inputShape.dim(2) + 2 * desc.m_PadBottom - (desc.m_DilationX * (kernelH - 1) + 1)) /
telsoa01c577f2c2018-08-31 09:22:23 +0100523 static_cast<float>(desc.m_StrideY)) + 1));
telsoa014fcda012018-03-09 14:13:49 +0000524 outputShape.add_dim(3);
525 outputShape.set_dim(
telsoa01c577f2c2018-08-31 09:22:23 +0100526 3, (static_cast<int>(
Keith Mok7dc18202020-12-20 13:45:51 -0800527 static_cast<float>(inputShape.dim(3) + 2 * desc.m_PadRight - (desc.m_DilationY * (kernelW - 1) + 1)) /
telsoa01c577f2c2018-08-31 09:22:23 +0100528 static_cast<float>(desc.m_StrideX)) + 1));
telsoa014fcda012018-03-09 14:13:49 +0000529
530 // Load the weight data for ALL groups
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100531 vector<float> weightData(armnn::numeric_cast<size_t>(numGroups *
telsoa01c577f2c2018-08-31 09:22:23 +0100532 inputShape.dim(1) * // number of input channels
533 outputShape.dim(1) * // number of output channels
534 kernelH *
535 kernelW));
telsoa014fcda012018-03-09 14:13:49 +0000536 GetDataFromBlob(layerParam, weightData, 0);
537
538 const unsigned int weightDimSizes[4] = {
telsoa01c577f2c2018-08-31 09:22:23 +0100539 static_cast<unsigned int>(outputShape.dim(1)),
540 static_cast<unsigned int>(inputShape.dim(1)),
541 kernelH,
542 kernelW};
telsoa014fcda012018-03-09 14:13:49 +0000543
telsoa014fcda012018-03-09 14:13:49 +0000544 TensorInfo biasInfo;
545 vector<float> biasData;
telsoa01c577f2c2018-08-31 09:22:23 +0100546
547 if (desc.m_BiasEnabled)
telsoa014fcda012018-03-09 14:13:49 +0000548 {
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100549 biasData.resize(armnn::numeric_cast<size_t>(numGroups * outputShape.dim(1)), 1.f);
telsoa014fcda012018-03-09 14:13:49 +0000550 GetDataFromBlob(layerParam, biasData, 1);
551
552 const unsigned int biasDimSizes[1] = {static_cast<unsigned int>(outputShape.dim(1))};
553 biasInfo = TensorInfo(1, biasDimSizes, DataType::Float32);
554 }
555
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100556 const unsigned int numWeightsPerGroup = armnn::numeric_cast<unsigned int>(weightData.size()) / numGroups;
557 const unsigned int numBiasesPerGroup = armnn::numeric_cast<unsigned int>(biasData.size()) / numGroups;
telsoa014fcda012018-03-09 14:13:49 +0000558
telsoa014fcda012018-03-09 14:13:49 +0000559 for (unsigned int g = 0; g < numGroups; ++g)
560 {
telsoa01c577f2c2018-08-31 09:22:23 +0100561 // Sets the slot index, group 0 should be connected to the 0th output of the splitter
562 // group 1 should be connected to the 1st output of the splitter.
telsoa014fcda012018-03-09 14:13:49 +0000563
telsoa01c577f2c2018-08-31 09:22:23 +0100564 // Pulls out the weights for this group from that loaded from the model file earlier.
telsoa014fcda012018-03-09 14:13:49 +0000565 ConstTensor weights(TensorInfo(4, weightDimSizes, DataType::Float32),
566 weightData.data() + numWeightsPerGroup * g);
567
568 IConnectableLayer* convLayer = nullptr;
Matteo Martincighfc598e12019-05-14 10:36:13 +0100569 Optional<ConstTensor> optionalBiases;
telsoa01c577f2c2018-08-31 09:22:23 +0100570 if (desc.m_BiasEnabled)
telsoa014fcda012018-03-09 14:13:49 +0000571 {
telsoa01c577f2c2018-08-31 09:22:23 +0100572 // Pulls out the biases for this group from that loaded from the model file earlier.
telsoa014fcda012018-03-09 14:13:49 +0000573 ConstTensor biases(biasInfo, biasData.data() + numBiasesPerGroup * g);
Matteo Martincighfc598e12019-05-14 10:36:13 +0100574 optionalBiases = Optional<ConstTensor>(biases);
telsoa014fcda012018-03-09 14:13:49 +0000575 }
Matteo Martincighfc598e12019-05-14 10:36:13 +0100576 convLayer = m_Network->AddConvolution2dLayer(desc,
577 weights,
578 optionalBiases,
579 convLayerNames[g].c_str());
telsoa014fcda012018-03-09 14:13:49 +0000580 convLayers[g] = convLayer;
581
582 // If we have more than one group then the input to the nth convolution the splitter layer's nth output,
583 // otherwise it's the regular input to this layer.
telsoa01c577f2c2018-08-31 09:22:23 +0100584 armnn::IOutputSlot& splitterInputConnection =
585 splitterLayer ? splitterLayer->GetOutputSlot(g) : inputConnection;
telsoa014fcda012018-03-09 14:13:49 +0000586 splitterInputConnection.Connect(convLayer->GetInputSlot(0));
587 convLayer->GetOutputSlot(0).SetTensorInfo(BlobShapeToTensorInfo(outputShape));
telsoa014fcda012018-03-09 14:13:49 +0000588 }
589
Jim Flynne242f2d2019-05-22 14:24:13 +0100590 // If the convolution was performed in chunks, add a layer to concatenate the results
telsoa01c577f2c2018-08-31 09:22:23 +0100591
592 // The merge input shape matches that of the convolution output
Jim Flynne242f2d2019-05-22 14:24:13 +0100593 unsigned int concatDimSizes[4] = {static_cast<unsigned int>(outputShape.dim(0)),
594 static_cast<unsigned int>(outputShape.dim(1)),
595 static_cast<unsigned int>(outputShape.dim(2)),
596 static_cast<unsigned int>(outputShape.dim(3))};
telsoa01c577f2c2018-08-31 09:22:23 +0100597
Jim Flynne242f2d2019-05-22 14:24:13 +0100598 // This is used to describe how the input is to be concatenated
599 OriginsDescriptor concatDesc(numGroups);
telsoa01c577f2c2018-08-31 09:22:23 +0100600
601 // Now create an input node for each group, using the name from
602 // the output of the corresponding convolution
603 for (unsigned int g = 0; g < numGroups; ++g)
telsoa014fcda012018-03-09 14:13:49 +0000604 {
Jim Flynne242f2d2019-05-22 14:24:13 +0100605 concatDesc.SetViewOriginCoord(g, 1, concatDimSizes[1] * g);
telsoa01c577f2c2018-08-31 09:22:23 +0100606 }
telsoa014fcda012018-03-09 14:13:49 +0000607
Jim Flynne242f2d2019-05-22 14:24:13 +0100608 // Make sure the output from the concat is the correct size to hold the data for all groups
609 concatDimSizes[1] *= numGroups;
610 outputShape.set_dim(1, concatDimSizes[1]);
telsoa014fcda012018-03-09 14:13:49 +0000611
Jim Flynne242f2d2019-05-22 14:24:13 +0100612 // Finally add the concat layer
613 IConnectableLayer* concatLayer = m_Network->AddConcatLayer(concatDesc, layerParam.name().c_str());
telsoa014fcda012018-03-09 14:13:49 +0000614
Jim Flynne242f2d2019-05-22 14:24:13 +0100615 if (!concatLayer)
telsoa01c577f2c2018-08-31 09:22:23 +0100616 {
617 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100618 fmt::format("Failed to create final concat layer for Split+Convolution+Concat. "
619 "Layer={} #groups={} #filters={} {}",
620 layerParam.name(),
621 numGroups,
622 numFilters,
623 CHECK_LOCATION().AsString()));
telsoa01c577f2c2018-08-31 09:22:23 +0100624 }
telsoa014fcda012018-03-09 14:13:49 +0000625
telsoa01c577f2c2018-08-31 09:22:23 +0100626 for (unsigned int g = 0; g < numGroups; ++g)
627 {
Jim Flynne242f2d2019-05-22 14:24:13 +0100628 convLayers[g]->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(g));
telsoa01c577f2c2018-08-31 09:22:23 +0100629 }
Jim Flynne242f2d2019-05-22 14:24:13 +0100630 concatLayer->GetOutputSlot(0).SetTensorInfo(armnn::TensorInfo(4, concatDimSizes, DataType::Float32));
631 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), concatLayer->GetOutputSlot(0));
telsoa01c577f2c2018-08-31 09:22:23 +0100632}
telsoa014fcda012018-03-09 14:13:49 +0000633
Kevin Mayef33cb12021-01-29 14:24:57 +0000634void ICaffeParser::CaffeParserImpl::AddDeconvLayerWithSplits(const caffe::LayerParameter& layerParam,
635 const armnn::TransposeConvolution2dDescriptor& desc,
636 unsigned int kernelW,
637 unsigned int kernelH)
Keith Mok9801b172020-12-20 19:47:25 -0800638{
639 ARMNN_ASSERT(layerParam.type() == "Deconvolution");
640 ValidateNumInputsOutputs(layerParam, 1, 1);
641
642 ConvolutionParameter convParam = layerParam.convolution_param();
643 BlobShape inputShape = TensorDescToBlobShape(GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo());
644 const unsigned int numGroups = convParam.has_group() ? convParam.group() : 1;
645
646 // asusme these were already verified by the caller ParseDeconvLayer() function
647 ARMNN_ASSERT(numGroups <= inputShape.dim(1));
648 ARMNN_ASSERT(numGroups > 1);
649
650 // Handle grouping
651 armnn::IOutputSlot& inputConnection = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0));
652
653 vector<string> convLayerNames(numGroups);
654 vector<armnn::IConnectableLayer*> convLayers(numGroups);
655 convLayerNames[0] = layerParam.name();
656
657 // This deconvolution is to be applied to chunks of the input data so add a splitter layer
658
659 // Redirect the deconvolution input to the splitter
660 unsigned int splitterDimSizes[4] = {static_cast<unsigned int>(inputShape.dim(0)),
661 static_cast<unsigned int>(inputShape.dim(1)),
662 static_cast<unsigned int>(inputShape.dim(2)),
663 static_cast<unsigned int>(inputShape.dim(3))};
664
665 // Split dimension 1 of the splitter output shape and deconv input shapes
666 // according to the number of groups
667
668 splitterDimSizes[1] /= numGroups;
669 inputShape.set_dim(1, splitterDimSizes[1]);
670
671 // This is used to describe how the input is to be split
672 ViewsDescriptor splitterDesc(numGroups);
673
674 // Create an output node for each group, giving each a unique name
675 for (unsigned int g = 0; g < numGroups; ++g)
676 {
677 // Work out the names of the splitter layers child deconvolutions
678 stringstream ss;
679 ss << layerParam.name() << "_" << g;
680 convLayerNames[g] = ss.str();
681
682 splitterDesc.SetViewOriginCoord(g, 1, splitterDimSizes[1] * g);
683
684 // Set the size of the views.
685 for (unsigned int dimIdx=0; dimIdx < 4; dimIdx++)
686 {
687 splitterDesc.SetViewSize(g, dimIdx, splitterDimSizes[dimIdx]);
688 }
689 }
690
691 const std::string splitterLayerName = std::string("splitter_") + layerParam.bottom(0);
692 armnn::IConnectableLayer* splitterLayer = m_Network->AddSplitterLayer(splitterDesc, splitterLayerName.c_str());
693
694 inputConnection.Connect(splitterLayer->GetInputSlot(0));
695 for (unsigned int i = 0; i < splitterLayer->GetNumOutputSlots(); i++)
696 {
697 splitterLayer->GetOutputSlot(i).SetTensorInfo(BlobShapeToTensorInfo(inputShape));
698 }
699
700 unsigned int numFilters = convParam.num_output();
701
702 // Populates deconvolution output tensor descriptor dimensions.
703 BlobShape outputShape;
704 outputShape.add_dim(0);
705 outputShape.set_dim(0, inputShape.dim(0));
706 outputShape.add_dim(1);
707 // Ensures that dimension 1 of the deconvolution output is split according to the number of groups.
708 outputShape.set_dim(1, numFilters / numGroups);
709 outputShape.add_dim(2);
710 outputShape.set_dim(
711 2, (static_cast<int>(
712 desc.m_StrideY * (inputShape.dim(2) - 1) - 2 * desc.m_PadBottom + kernelH)));
713 outputShape.add_dim(3);
714 outputShape.set_dim(
715 3, (static_cast<int>(
716 desc.m_StrideX * (inputShape.dim(3) - 1) - 2 * desc.m_PadRight + kernelW)));
717
718 // Load the weight data for ALL groups
719 vector<float> weightData(armnn::numeric_cast<size_t>(numGroups *
720 inputShape.dim(1) * // number of input channels
721 outputShape.dim(1) * // number of output channels
722 kernelH *
723 kernelW));
724 GetDataFromBlob(layerParam, weightData, 0);
725
726 const unsigned int weightDimSizes[4] = {
727 static_cast<unsigned int>(outputShape.dim(1)),
728 static_cast<unsigned int>(inputShape.dim(1)),
729 kernelH,
730 kernelW};
731
732 TensorInfo biasInfo;
733 vector<float> biasData;
734
735 if (desc.m_BiasEnabled)
736 {
737 biasData.resize(armnn::numeric_cast<size_t>(numGroups * outputShape.dim(1)), 1.f);
738 GetDataFromBlob(layerParam, biasData, 1);
739
740 const unsigned int biasDimSizes[1] = {static_cast<unsigned int>(outputShape.dim(1))};
741 biasInfo = TensorInfo(1, biasDimSizes, DataType::Float32);
742 }
743
744 const unsigned int numWeightsPerGroup = armnn::numeric_cast<unsigned int>(weightData.size()) / numGroups;
745 const unsigned int numBiasesPerGroup = armnn::numeric_cast<unsigned int>(biasData.size()) / numGroups;
746
747 for (unsigned int g = 0; g < numGroups; ++g)
748 {
749 // Sets the slot index, group 0 should be connected to the 0th output of the splitter
750 // group 1 should be connected to the 1st output of the splitter.
751
752 // Pulls out the weights for this group from that loaded from the model file earlier.
753 ConstTensor weights(TensorInfo(4, weightDimSizes, DataType::Float32),
754 weightData.data() + numWeightsPerGroup * g);
755
756 IConnectableLayer* deconvLayer = nullptr;
757 Optional<ConstTensor> optionalBiases;
758 if (desc.m_BiasEnabled)
759 {
760 // Pulls out the biases for this group from that loaded from the model file earlier.
761 ConstTensor biases(biasInfo, biasData.data() + numBiasesPerGroup * g);
762 optionalBiases = Optional<ConstTensor>(biases);
763 }
764 deconvLayer = m_Network->AddTransposeConvolution2dLayer(desc,
765 weights,
766 optionalBiases,
767 convLayerNames[g].c_str());
768 convLayers[g] = deconvLayer;
769
770 // If we have more than one group then the input to the nth deconvolution the splitter layer's nth output,
771 // otherwise it's the regular input to this layer.
772 armnn::IOutputSlot& splitterInputConnection =
773 splitterLayer ? splitterLayer->GetOutputSlot(g) : inputConnection;
774 splitterInputConnection.Connect(deconvLayer->GetInputSlot(0));
775 deconvLayer->GetOutputSlot(0).SetTensorInfo(BlobShapeToTensorInfo(outputShape));
776 }
777
778 // If the deconvolution was performed in chunks, add a layer to concatenate the results
779
780 // The merge input shape matches that of the deconvolution output
781 unsigned int concatDimSizes[4] = {static_cast<unsigned int>(outputShape.dim(0)),
782 static_cast<unsigned int>(outputShape.dim(1)),
783 static_cast<unsigned int>(outputShape.dim(2)),
784 static_cast<unsigned int>(outputShape.dim(3))};
785
786 // This is used to describe how the input is to be concatenated
787 OriginsDescriptor concatDesc(numGroups);
788
789 // Now create an input node for each group, using the name from
790 // the output of the corresponding deconvolution
791 for (unsigned int g = 0; g < numGroups; ++g)
792 {
793 concatDesc.SetViewOriginCoord(g, 1, concatDimSizes[1] * g);
794 }
795
796 // Make sure the output from the concat is the correct size to hold the data for all groups
797 concatDimSizes[1] *= numGroups;
798 outputShape.set_dim(1, concatDimSizes[1]);
799
800 // Finally add the concat layer
801 IConnectableLayer* concatLayer = m_Network->AddConcatLayer(concatDesc, layerParam.name().c_str());
802
803 if (!concatLayer)
804 {
805 throw ParseException(
806 fmt::format("Failed to create final concat layer for Split+Deconvolution+Concat. "
807 "Layer={} #groups={} #filters={} {}",
808 layerParam.name(),
809 numGroups,
810 numFilters,
811 CHECK_LOCATION().AsString()));
812 }
813
814 for (unsigned int g = 0; g < numGroups; ++g)
815 {
816 convLayers[g]->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(g));
817 }
818 concatLayer->GetOutputSlot(0).SetTensorInfo(armnn::TensorInfo(4, concatDimSizes, DataType::Float32));
819 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), concatLayer->GetOutputSlot(0));
820}
821
Kevin Mayef33cb12021-01-29 14:24:57 +0000822void ICaffeParser::CaffeParserImpl::AddConvLayerWithDepthwiseConv(const caffe::LayerParameter& layerParam,
823 const armnn::Convolution2dDescriptor& convDesc,
824 unsigned int kernelW,
825 unsigned int kernelH)
telsoa01c577f2c2018-08-31 09:22:23 +0100826{
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100827 ARMNN_ASSERT(layerParam.type() == "Convolution");
telsoa01c577f2c2018-08-31 09:22:23 +0100828 ValidateNumInputsOutputs(layerParam, 1, 1);
telsoa014fcda012018-03-09 14:13:49 +0000829
telsoa01c577f2c2018-08-31 09:22:23 +0100830 ConvolutionParameter convParam = layerParam.convolution_param();
831 BlobShape inputShape = TensorDescToBlobShape(GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo());
telsoa014fcda012018-03-09 14:13:49 +0000832
telsoa01c577f2c2018-08-31 09:22:23 +0100833 DepthwiseConvolution2dDescriptor desc;
834 desc.m_PadLeft = convDesc.m_PadLeft;
835 desc.m_PadRight = convDesc.m_PadRight;
836 desc.m_PadTop = convDesc.m_PadTop;
837 desc.m_PadBottom = convDesc.m_PadBottom;
838 desc.m_StrideX = convDesc.m_StrideX;
839 desc.m_StrideY = convDesc.m_StrideY;
Keith Mok7dc18202020-12-20 13:45:51 -0800840 desc.m_DilationX = convDesc.m_DilationX;
841 desc.m_DilationY = convDesc.m_DilationY;
telsoa01c577f2c2018-08-31 09:22:23 +0100842 desc.m_BiasEnabled = convDesc.m_BiasEnabled;
telsoa014fcda012018-03-09 14:13:49 +0000843
telsoa01c577f2c2018-08-31 09:22:23 +0100844 unsigned int numFilters = convParam.num_output();
845
846 BlobShape outputShape;
847 outputShape.add_dim(0);
848 outputShape.set_dim(0, inputShape.dim(0));
849 outputShape.add_dim(1);
850 outputShape.set_dim(1, numFilters);
851 outputShape.add_dim(2);
852 outputShape.set_dim(
853 2, (static_cast<int>(
Keith Mok7dc18202020-12-20 13:45:51 -0800854 static_cast<float>(inputShape.dim(2) + 2 * desc.m_PadBottom - (desc.m_DilationX * (kernelH - 1) + 1)) /
telsoa01c577f2c2018-08-31 09:22:23 +0100855 static_cast<float>(desc.m_StrideY)) + 1));
856 outputShape.add_dim(3);
857 outputShape.set_dim(
858 3, (static_cast<int>(
Keith Mok7dc18202020-12-20 13:45:51 -0800859 static_cast<float>(inputShape.dim(3) + 2 * desc.m_PadRight - (desc.m_DilationY * (kernelW - 1) + 1)) /
telsoa01c577f2c2018-08-31 09:22:23 +0100860 static_cast<float>(desc.m_StrideX)) + 1));
861
862 // Load the weight data
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100863 size_t allWeightsSize = armnn::numeric_cast<size_t>(inputShape.dim(1) * kernelH * kernelW);
telsoa01c577f2c2018-08-31 09:22:23 +0100864 vector<float> weightData(allWeightsSize);
865
866 GetDataFromBlob(layerParam, weightData, 0);
867
868 // depth multiplier will be 1 for the depthwise convolution
869 const unsigned int weightDimSizes[4] = {
870 static_cast<unsigned int>(1), // depth multiplier
871 static_cast<unsigned int>(inputShape.dim(1)), // #channels
872 kernelH,
873 kernelW};
874
875 armnn::IConnectableLayer* returnLayer = nullptr;
876 ConstTensor weights(TensorInfo(4, weightDimSizes, DataType::Float32), weightData.data());
Matteo Martincighfc598e12019-05-14 10:36:13 +0100877 Optional<ConstTensor> optionalBiases;
878 vector<float> biasData;
telsoa01c577f2c2018-08-31 09:22:23 +0100879 if (desc.m_BiasEnabled)
880 {
881 TensorInfo biasInfo;
telsoa01c577f2c2018-08-31 09:22:23 +0100882
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100883 biasData.resize(armnn::numeric_cast<size_t>(outputShape.dim(1)), 1.f);
telsoa01c577f2c2018-08-31 09:22:23 +0100884 GetDataFromBlob(layerParam, biasData, 1);
885
886 const unsigned int biasDimSizes[1] = {static_cast<unsigned int>(outputShape.dim(1))};
887 biasInfo = TensorInfo(1, biasDimSizes, DataType::Float32);
888
889 ConstTensor biases(biasInfo, biasData.data());
Matteo Martincighfc598e12019-05-14 10:36:13 +0100890 optionalBiases = Optional<ConstTensor>(biases);
telsoa01c577f2c2018-08-31 09:22:23 +0100891 }
Matteo Martincighfc598e12019-05-14 10:36:13 +0100892 returnLayer = m_Network->AddDepthwiseConvolution2dLayer(desc,
893 weights,
894 optionalBiases,
895 layerParam.name().c_str());
telsoa014fcda012018-03-09 14:13:49 +0000896
surmeh013537c2c2018-05-18 16:31:43 +0100897 if (!returnLayer)
898 {
telsoa01c577f2c2018-08-31 09:22:23 +0100899 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100900 fmt::format("Failed to create depthwise convolution layer. "
901 "Layer={} #filters={} {}",
902 layerParam.name(),
903 numFilters,
904 CHECK_LOCATION().AsString()));
telsoa01c577f2c2018-08-31 09:22:23 +0100905 }
906 armnn::IOutputSlot& inputConnection = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0));
907 inputConnection.Connect(returnLayer->GetInputSlot(0));
908 returnLayer->GetOutputSlot(0).SetTensorInfo(BlobShapeToTensorInfo(outputShape));
909 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), returnLayer->GetOutputSlot(0));
910}
911
Kevin Mayef33cb12021-01-29 14:24:57 +0000912void ICaffeParser::CaffeParserImpl::ParseConvLayer(const LayerParameter& layerParam)
telsoa01c577f2c2018-08-31 09:22:23 +0100913{
914 // Ignored Caffe Parameters
telsoa01c577f2c2018-08-31 09:22:23 +0100915 // * Weight Filler
916 // * Bias Filler
917 // * Engine
918 // * Force nd_im2col
919 // * Axis
920
921 // Not Available ArmNN Interface Parameters
922 // * Rounding policy;
923
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100924 ARMNN_ASSERT(layerParam.type() == "Convolution");
telsoa01c577f2c2018-08-31 09:22:23 +0100925 ValidateNumInputsOutputs(layerParam, 1, 1);
926
927 ConvolutionParameter convParam = layerParam.convolution_param();
928 BlobShape inputShape = TensorDescToBlobShape(GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo());
929 const unsigned int numGroups = convParam.has_group() ? convParam.group() : 1;
930 unsigned int numFilters = convParam.num_output();
931
932 const auto notFound = std::numeric_limits<unsigned int>::max();
933
934 unsigned int kernelH = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
935 kernel_h, kernel_size, unsigned int, notFound);
936 unsigned int kernelW = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
937 kernel_w, kernel_size, unsigned int, notFound);
938
939 unsigned int strideH = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
940 stride_h, stride, unsigned int, 1u);
941 unsigned int strideW = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
942 stride_w, stride, unsigned int, 1u);
943
944 unsigned int padH = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
945 pad_h, pad, unsigned int, 0u);
946 unsigned int padW = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
947 pad_w, pad, unsigned int, 0u);
948
Keith Mok7dc18202020-12-20 13:45:51 -0800949 unsigned int dilationH = convParam.dilation_size() > 0 ? convParam.dilation(0) : 1;
950 unsigned int dilationW = convParam.dilation_size() > 1 ? convParam.dilation(1) :
951 convParam.dilation_size() > 0 ? convParam.dilation(0) : 1;
952
telsoa01c577f2c2018-08-31 09:22:23 +0100953 Convolution2dDescriptor convolution2dDescriptor;
954 convolution2dDescriptor.m_PadLeft = padW;
955 convolution2dDescriptor.m_PadRight = padW;
956 convolution2dDescriptor.m_PadTop = padH;
957 convolution2dDescriptor.m_PadBottom = padH;
958 convolution2dDescriptor.m_StrideX = strideW;
959 convolution2dDescriptor.m_StrideY = strideH;
Keith Mok7dc18202020-12-20 13:45:51 -0800960 convolution2dDescriptor.m_DilationX = dilationW;
961 convolution2dDescriptor.m_DilationY = dilationH;
telsoa01c577f2c2018-08-31 09:22:23 +0100962 convolution2dDescriptor.m_BiasEnabled = convParam.has_bias_term() ? convParam.bias_term() : true;
963
964 if (numGroups > numFilters)
965 {
966 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100967 fmt::format("Error parsing Convolution: {}. "
968 "The 'group'={} parameter cannot be larger than the "
969 "number of filters supplied ='{}'. {}",
970 layerParam.name(),
971 numGroups,
972 numFilters,
973 CHECK_LOCATION().AsString()));
telsoa01c577f2c2018-08-31 09:22:23 +0100974 }
975
976 if (inputShape.dim_size() != 4)
977 {
978 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100979 fmt::format("Convolution input shape is expected to have 4 dimensions. "
980 "{}'s input has only {}. {}",
981 layerParam.name(),
982 inputShape.dim_size(),
983 CHECK_LOCATION().AsString()));
telsoa01c577f2c2018-08-31 09:22:23 +0100984 }
985
986 if (numGroups > 1)
987 {
988 if (numGroups > inputShape.dim(1))
989 {
990 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100991 fmt::format("Error parsing Convolution: {}. "
992 "The 'group'={} parameter cannot be larger than the "
993 "channel of the input shape={} (in NCHW format). {}",
994 layerParam.name(),
995 numGroups,
996 inputShape.dim(1),
997 CHECK_LOCATION().AsString()));
telsoa01c577f2c2018-08-31 09:22:23 +0100998 }
999 else if (numGroups == inputShape.dim(1))
1000 {
1001 // we use a depthwise convolution here, because the number of groups equals to the
1002 // input channels
1003 AddConvLayerWithDepthwiseConv(layerParam, convolution2dDescriptor, kernelW, kernelH);
1004 return;
1005 }
1006 else
1007 {
1008 // we split the input by channels into channels/groups separate convolutions
Jim Flynne242f2d2019-05-22 14:24:13 +01001009 // and concatenate the results afterwards
telsoa01c577f2c2018-08-31 09:22:23 +01001010 AddConvLayerWithSplits(layerParam, convolution2dDescriptor, kernelW, kernelH);
1011 return;
1012 }
1013 }
1014
1015 // NOTE: at this point we only need to handle #group=1 case, all other cases should be
1016 // handled by the AddConvLayer* helpers
1017
1018 // Populate convolution output tensor descriptor dimensions
1019 BlobShape outputShape;
1020 outputShape.add_dim(0);
1021 outputShape.set_dim(0, inputShape.dim(0));
1022 outputShape.add_dim(1);
1023 outputShape.set_dim(1, numFilters);
1024 outputShape.add_dim(2);
1025 outputShape.set_dim(
1026 2, (static_cast<int>(
Keith Mok7dc18202020-12-20 13:45:51 -08001027 static_cast<float>(inputShape.dim(2) + 2 * padH - (dilationH * (kernelH - 1) + 1)) /
telsoa01c577f2c2018-08-31 09:22:23 +01001028 static_cast<float>(strideH)) + 1));
1029 outputShape.add_dim(3);
1030 outputShape.set_dim(
1031 3, (static_cast<int>(
Keith Mok7dc18202020-12-20 13:45:51 -08001032 static_cast<float>(inputShape.dim(3) + 2 * padW - (dilationW * (kernelW - 1) + 1)) /
telsoa01c577f2c2018-08-31 09:22:23 +01001033 static_cast<float>(strideW)) + 1));
1034
1035 // Load the weight data for ALL groups
Matthew Sloyan589e3e82020-09-11 16:17:48 +01001036 vector<float> weightData(armnn::numeric_cast<size_t>(inputShape.dim(1) *
telsoa01c577f2c2018-08-31 09:22:23 +01001037 outputShape.dim(1) *
1038 kernelH *
1039 kernelW));
1040 GetDataFromBlob(layerParam, weightData, 0);
1041
1042 const unsigned int weightDimSizes[4] = {
1043 static_cast<unsigned int>(outputShape.dim(1)), // output channels
1044 static_cast<unsigned int>(inputShape.dim(1)), // input channels
1045 kernelH,
1046 kernelW};
1047
1048 armnn::IConnectableLayer* returnLayer = nullptr;
1049
1050 // Pull out the weights for this group from that loaded from the model file earlier
1051 ConstTensor weights(TensorInfo(4, weightDimSizes, DataType::Float32), weightData.data());
Matteo Martincighfc598e12019-05-14 10:36:13 +01001052 Optional<ConstTensor> optionalBiases;
1053 vector<float> biasData;
telsoa01c577f2c2018-08-31 09:22:23 +01001054 if (convolution2dDescriptor.m_BiasEnabled)
1055 {
1056 TensorInfo biasInfo;
telsoa01c577f2c2018-08-31 09:22:23 +01001057
Matthew Sloyan589e3e82020-09-11 16:17:48 +01001058 biasData.resize(armnn::numeric_cast<size_t>(outputShape.dim(1)), 1.f);
telsoa01c577f2c2018-08-31 09:22:23 +01001059 GetDataFromBlob(layerParam, biasData, 1);
1060
1061 const unsigned int biasDimSizes[1] = {static_cast<unsigned int>(outputShape.dim(1))};
1062 biasInfo = TensorInfo(1, biasDimSizes, DataType::Float32);
1063
1064 // Pull out the biases for this group from that loaded from the model file earlier
1065 ConstTensor biases(biasInfo, biasData.data());
Matteo Martincighfc598e12019-05-14 10:36:13 +01001066 optionalBiases = Optional<ConstTensor>(biases);
telsoa01c577f2c2018-08-31 09:22:23 +01001067 }
Matteo Martincighfc598e12019-05-14 10:36:13 +01001068 returnLayer = m_Network->AddConvolution2dLayer(convolution2dDescriptor,
1069 weights,
1070 optionalBiases,
1071 layerParam.name().c_str());
telsoa01c577f2c2018-08-31 09:22:23 +01001072
1073 armnn::IOutputSlot& inputConnection = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0));
1074 inputConnection.Connect(returnLayer->GetInputSlot(0));
1075 returnLayer->GetOutputSlot(0).SetTensorInfo(BlobShapeToTensorInfo(outputShape));
1076
1077 if (!returnLayer)
1078 {
1079 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001080 fmt::format("Failed to create Convolution layer. "
1081 "Layer={} #groups={} #filters={} {}",
1082 layerParam.name(),
1083 numGroups,
1084 numFilters,
1085 CHECK_LOCATION().AsString()));
surmeh013537c2c2018-05-18 16:31:43 +01001086 }
1087
telsoa014fcda012018-03-09 14:13:49 +00001088 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), returnLayer->GetOutputSlot(0));
1089}
1090
Kevin Mayef33cb12021-01-29 14:24:57 +00001091void ICaffeParser::CaffeParserImpl::ParseDeconvLayer(const LayerParameter& layerParam)
Keith Mok9801b172020-12-20 19:47:25 -08001092{
1093 // Ignored Caffe Parameters
1094 // * Weight Filler
1095 // * Bias Filler
1096 // * Engine
1097 // * Force nd_im2col
1098 // * Axis
1099
1100 // Not Available ArmNN Interface Parameters
1101 // * Rounding policy;
1102
1103 ARMNN_ASSERT(layerParam.type() == "Deconvolution");
1104 ValidateNumInputsOutputs(layerParam, 1, 1);
1105
1106 ConvolutionParameter convParam = layerParam.convolution_param();
1107 BlobShape inputShape = TensorDescToBlobShape(GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo());
1108 const unsigned int numGroups = convParam.has_group() ? convParam.group() : 1;
1109 unsigned int numFilters = convParam.num_output();
1110
1111 const auto notFound = std::numeric_limits<unsigned int>::max();
1112
1113 unsigned int kernelH = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
1114 kernel_h, kernel_size, unsigned int, notFound);
1115 unsigned int kernelW = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
1116 kernel_w, kernel_size, unsigned int, notFound);
1117
1118 unsigned int strideH = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
1119 stride_h, stride, unsigned int, 1u);
1120 unsigned int strideW = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
1121 stride_w, stride, unsigned int, 1u);
1122
1123 unsigned int padH = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
1124 pad_h, pad, unsigned int, 0u);
1125 unsigned int padW = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
1126 pad_w, pad, unsigned int, 0u);
1127
1128 unsigned int dilationH = convParam.dilation_size() > 0 ? convParam.dilation(0) : 1;
1129 unsigned int dilationW = convParam.dilation_size() > 1 ? convParam.dilation(1) :
1130 convParam.dilation_size() > 0 ? convParam.dilation(0) : 1;
1131
1132 if (dilationH != 1 || dilationW != 1) {
1133 fmt::format("Dilated decnvolution is not supported. "
1134 "{}'s input has dilation {} {}. {}",
1135 layerParam.name(),
1136 dilationW, dilationH,
1137 CHECK_LOCATION().AsString());
1138 }
1139
1140 TransposeConvolution2dDescriptor deconvolution2dDescriptor;
1141 deconvolution2dDescriptor.m_PadLeft = padW;
1142 deconvolution2dDescriptor.m_PadRight = padW;
1143 deconvolution2dDescriptor.m_PadTop = padH;
1144 deconvolution2dDescriptor.m_PadBottom = padH;
1145 deconvolution2dDescriptor.m_StrideX = strideW;
1146 deconvolution2dDescriptor.m_StrideY = strideH;
1147 deconvolution2dDescriptor.m_BiasEnabled = convParam.has_bias_term() ? convParam.bias_term() : true;
1148
1149 if (numGroups > numFilters)
1150 {
1151 throw ParseException(
1152 fmt::format("Error parsing Deconvolution: {}. "
1153 "The 'group'={} parameter cannot be larger than the "
1154 "number of filters supplied ='{}'. {}",
1155 layerParam.name(),
1156 numGroups,
1157 numFilters,
1158 CHECK_LOCATION().AsString()));
1159 }
1160
1161 if (inputShape.dim_size() != 4)
1162 {
1163 throw ParseException(
1164 fmt::format("Deconvolution input shape is expected to have 4 dimensions. "
1165 "{}'s input has only {}. {}",
1166 layerParam.name(),
1167 inputShape.dim_size(),
1168 CHECK_LOCATION().AsString()));
1169 }
1170
1171 if (numGroups > 1)
1172 {
1173 if (numGroups > inputShape.dim(1))
1174 {
1175 throw ParseException(
1176 fmt::format("Error parsing Deconvolution: {}. "
1177 "The 'group'={} parameter cannot be larger than the "
1178 "channel of the input shape={} (in NCHW format). {}",
1179 layerParam.name(),
1180 numGroups,
1181 inputShape.dim(1),
1182 CHECK_LOCATION().AsString()));
1183 }
1184 else
1185 {
1186 // we split the input by channels into channels/groups separate convolutions
1187 // and concatenate the results afterwards
1188 AddDeconvLayerWithSplits(layerParam, deconvolution2dDescriptor, kernelW, kernelH);
1189 return;
1190 }
1191 }
1192
1193 // NOTE: at this point we only need to handle #group=1 case, all other cases should be
1194 // handled by the AddDeconvLayer* helpers
1195
1196 // Populate deconvolution output tensor descriptor dimensions
1197 BlobShape outputShape;
1198 outputShape.add_dim(0);
1199 outputShape.set_dim(0, inputShape.dim(0));
1200 outputShape.add_dim(1);
1201 outputShape.set_dim(1, numFilters);
1202 outputShape.add_dim(2);
1203 outputShape.set_dim(
1204 2, (static_cast<int>(
1205 strideH * (inputShape.dim(2) - 1) - 2 * padH + (dilationH * (kernelH - 1) + 1))));
1206 outputShape.add_dim(3);
1207 outputShape.set_dim(
1208 3, (static_cast<int>(
1209 strideW * (inputShape.dim(3) - 1) - 2 * padW + (dilationW * (kernelW - 1) + 1))));
1210
1211 // Load the weight data for ALL groups
1212 vector<float> weightData(armnn::numeric_cast<size_t>(inputShape.dim(1) *
1213 outputShape.dim(1) *
1214 kernelH *
1215 kernelW));
1216 GetDataFromBlob(layerParam, weightData, 0);
1217
1218 const unsigned int weightDimSizes[4] = {
1219 static_cast<unsigned int>(outputShape.dim(1)), // output channels
1220 static_cast<unsigned int>(inputShape.dim(1)), // input channels
1221 kernelH,
1222 kernelW};
1223
1224 armnn::IConnectableLayer* returnLayer = nullptr;
1225
1226 // Pull out the weights for this group from that loaded from the model file earlier
1227 ConstTensor weights(TensorInfo(4, weightDimSizes, DataType::Float32), weightData.data());
1228 Optional<ConstTensor> optionalBiases;
1229 vector<float> biasData;
1230 if (deconvolution2dDescriptor.m_BiasEnabled)
1231 {
1232 TensorInfo biasInfo;
1233
1234 biasData.resize(armnn::numeric_cast<size_t>(outputShape.dim(1)), 1.f);
1235 GetDataFromBlob(layerParam, biasData, 1);
1236
1237 const unsigned int biasDimSizes[1] = {static_cast<unsigned int>(outputShape.dim(1))};
1238 biasInfo = TensorInfo(1, biasDimSizes, DataType::Float32);
1239
1240 // Pull out the biases for this group from that loaded from the model file earlier
1241 ConstTensor biases(biasInfo, biasData.data());
1242 optionalBiases = Optional<ConstTensor>(biases);
1243 }
1244 returnLayer = m_Network->AddTransposeConvolution2dLayer(deconvolution2dDescriptor,
1245 weights,
1246 optionalBiases,
1247 layerParam.name().c_str());
1248
1249 armnn::IOutputSlot& inputConnection = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0));
1250 inputConnection.Connect(returnLayer->GetInputSlot(0));
1251 returnLayer->GetOutputSlot(0).SetTensorInfo(BlobShapeToTensorInfo(outputShape));
1252
1253 if (!returnLayer)
1254 {
1255 throw ParseException(
1256 fmt::format("Failed to create Deconvolution layer. "
1257 "Layer={} #groups={} #filters={} {}",
1258 layerParam.name(),
1259 numGroups,
1260 numFilters,
1261 CHECK_LOCATION().AsString()));
1262 }
1263
1264 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), returnLayer->GetOutputSlot(0));
1265}
1266
Kevin Mayef33cb12021-01-29 14:24:57 +00001267void ICaffeParser::CaffeParserImpl::ParsePoolingLayer(const LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +00001268{
telsoa01c577f2c2018-08-31 09:22:23 +01001269 // Ignored Caffe Parameters
1270 // Stochastic Pooling
1271 // Engine
1272
telsoa014fcda012018-03-09 14:13:49 +00001273 ValidateNumInputsOutputs(layerParam, 1, 1);
telsoa014fcda012018-03-09 14:13:49 +00001274 PoolingParameter param = layerParam.pooling_param();
telsoa014fcda012018-03-09 14:13:49 +00001275 const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo();
1276
telsoa01c577f2c2018-08-31 09:22:23 +01001277 const auto notFound = std::numeric_limits<unsigned int>::max();
1278
1279 unsigned int kernel_h = GET_OPTIONAL_WITH_FALLBACK(param, PoolingParameter,
1280 kernel_h, kernel_size, unsigned int, notFound);
1281 unsigned int kernel_w = GET_OPTIONAL_WITH_FALLBACK(param, PoolingParameter,
1282 kernel_w, kernel_size, unsigned int, notFound);
1283
1284 if ((kernel_h == notFound || kernel_w == notFound) && param.has_global_pooling())
telsoa014fcda012018-03-09 14:13:49 +00001285 {
1286 kernel_h = inputInfo.GetShape()[2];
1287 kernel_w = inputInfo.GetShape()[3];
1288 }
telsoa01c577f2c2018-08-31 09:22:23 +01001289
telsoa01c577f2c2018-08-31 09:22:23 +01001290 unsigned int stride_h = GET_OPTIONAL_WITH_FALLBACK(param, PoolingParameter,
1291 stride_h, stride, unsigned int, notFound);
1292 unsigned int stride_w = GET_OPTIONAL_WITH_FALLBACK(param, PoolingParameter,
1293 stride_h, stride, unsigned int, notFound);
1294
1295 if ((stride_h == notFound || stride_w == notFound) && param.has_global_pooling())
telsoa014fcda012018-03-09 14:13:49 +00001296 {
telsoa01c577f2c2018-08-31 09:22:23 +01001297 stride_h = 1;
1298 stride_w = 1;
telsoa014fcda012018-03-09 14:13:49 +00001299 }
1300
telsoa01c577f2c2018-08-31 09:22:23 +01001301 unsigned int pad_h = GET_OPTIONAL_WITH_FALLBACK(param, PoolingParameter,
1302 pad_h, pad, unsigned int, 0u);
1303 unsigned int pad_w = GET_OPTIONAL_WITH_FALLBACK(param, PoolingParameter,
1304 pad_w, pad, unsigned int, 0u);
telsoa014fcda012018-03-09 14:13:49 +00001305
telsoa014fcda012018-03-09 14:13:49 +00001306 // Populate Weight and Bias Filter Descriptor
1307 Pooling2dDescriptor pooling2dDescriptor;
1308 if (param.has_pool())
1309 {
1310 PoolingParameter_PoolMethod p = param.pool();
1311 switch (p)
1312 {
1313 case PoolingParameter_PoolMethod_MAX:
1314 {
1315 pooling2dDescriptor.m_PoolType = PoolingAlgorithm::Max;
1316 break;
1317 }
1318 case PoolingParameter_PoolMethod_AVE:
1319 {
1320 pooling2dDescriptor.m_PoolType = PoolingAlgorithm::Average;
1321 break;
1322 }
1323 case PoolingParameter_PoolMethod_STOCHASTIC:
1324 {
telsoa01c577f2c2018-08-31 09:22:23 +01001325 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001326 fmt::format("Pooling Layer: Stochastic Pooling Not Supported. Layer={} {}",
1327 layerParam.name(),
1328 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001329 }
1330 default:
1331 {
telsoa01c577f2c2018-08-31 09:22:23 +01001332 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001333 fmt::format("Pooling Layer: unknown pooling method: {} for layer: {} {}",
1334 p,
1335 layerParam.name(),
1336 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001337 }
1338 }
1339 }
1340 else
1341 {
telsoa01c577f2c2018-08-31 09:22:23 +01001342 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001343 fmt::format("No Pooling Method Defined for {} {}",
1344 layerParam.name(),
1345 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001346 }
1347
1348 pooling2dDescriptor.m_PadLeft = pad_w;
1349 pooling2dDescriptor.m_PadRight = pad_w;
1350 pooling2dDescriptor.m_PadTop = pad_h;
1351 pooling2dDescriptor.m_PadBottom = pad_h;
1352 pooling2dDescriptor.m_StrideX = stride_w;
1353 pooling2dDescriptor.m_StrideY = stride_h;
1354 pooling2dDescriptor.m_PoolWidth = kernel_w;
1355 pooling2dDescriptor.m_PoolHeight = kernel_h;
1356
1357 pooling2dDescriptor.m_OutputShapeRounding = OutputShapeRounding::Ceiling;
1358 pooling2dDescriptor.m_PaddingMethod = PaddingMethod::IgnoreValue;
1359
1360 armnn::IConnectableLayer* poolingLayer = m_Network->AddPooling2dLayer(pooling2dDescriptor,
1361 layerParam.name().c_str());
1362
telsoa014fcda012018-03-09 14:13:49 +00001363 TensorInfo outputInfo(
1364 { inputInfo.GetShape()[0],
1365 inputInfo.GetShape()[1],
1366 static_cast<unsigned int>(ceil(
1367 static_cast<float>(inputInfo.GetShape()[2] + 2 * pad_h - kernel_h) /
Matthew Sloyan24ac8592020-09-23 16:57:23 +01001368 armnn::numeric_cast<float>(stride_h))) + 1,
telsoa014fcda012018-03-09 14:13:49 +00001369 static_cast<unsigned int>(ceil(
1370 static_cast<float>(inputInfo.GetShape()[3] + 2 * pad_w - kernel_w) /
Matthew Sloyan24ac8592020-09-23 16:57:23 +01001371 armnn::numeric_cast<float>(stride_w))) + 1 },
telsoa014fcda012018-03-09 14:13:49 +00001372 DataType::Float32);
1373
1374 GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).Connect(poolingLayer->GetInputSlot(0));
1375 poolingLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1376 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), poolingLayer->GetOutputSlot(0));
1377}
1378
Kevin Mayef33cb12021-01-29 14:24:57 +00001379void ICaffeParser::CaffeParserImpl::ParseArgmaxLayer(const LayerParameter& layerParam)
Keith Mok9801b172020-12-20 19:47:25 -08001380{
1381 ValidateNumInputsOutputs(layerParam, 1, 1);
1382 ArgMaxParameter param = layerParam.argmax_param();
1383
1384 BlobShape inputShape = TensorDescToBlobShape(GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo());
1385
1386 const unsigned int topK = param.has_top_k() ? param.top_k() : 1;
1387 if (topK != 1) {
1388 throw ParseException(
1389 fmt::format("ArgMaxLayer: Only support top_k equals to 1. Layer={} {}",
1390 layerParam.name(),
1391 CHECK_LOCATION().AsString()));
1392 }
1393
1394 const unsigned int outMaxVal = param.has_out_max_val() ? param.out_max_val() : false;
1395 if (outMaxVal) {
1396 throw ParseException(
1397 fmt::format("ArgMaxLayer: Does not support out_max_val. Layer={} {}",
1398 layerParam.name(),
1399 CHECK_LOCATION().AsString()));
1400 }
1401
1402 int axis = param.has_axis() ? param.axis() : 1;
1403 if (axis < 0) {
1404 axis = inputShape.dim_size() - axis;
1405 }
1406 if ((axis < 0) || (axis >= inputShape.dim_size())) {
1407 throw ParseException(
1408 fmt::format("ArgMaxLayer: Invalid axis value which outside range of input dims. "
1409 "{}'s input has input dim_size {}, requested axis: {}. {}",
1410 layerParam.name(),
1411 inputShape.dim_size(),
1412 axis,
1413 CHECK_LOCATION().AsString()));
1414 }
1415
1416 ArgMinMaxDescriptor desc;
1417 desc.m_Axis = axis;
1418 desc.m_Output_Type = armnn::DataType::Signed32;
1419 desc.m_Function = ArgMinMaxFunction::Max;
1420
1421 armnn::IConnectableLayer* argmaxLayer = m_Network->AddArgMinMaxLayer(desc,
1422 layerParam.name().c_str());
1423
1424 TensorShape outputShape(static_cast<unsigned int>(inputShape.dim_size() - 1));
1425 int j = 0;
1426 // remove the flatten axis
1427 for (int i = 0; i < inputShape.dim_size(); ++i)
1428 {
1429 if (i == axis) continue;
1430 outputShape[static_cast<unsigned int>(j++)] = static_cast<unsigned int>(inputShape.dim(i));
1431 }
1432 TensorInfo outputInfo(outputShape, DataType::Signed32);
1433
1434 GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).Connect(argmaxLayer->GetInputSlot(0));
1435 argmaxLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1436 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), argmaxLayer->GetOutputSlot(0));
1437}
1438
Kevin Mayef33cb12021-01-29 14:24:57 +00001439void ICaffeParser::CaffeParserImpl::ParseReluLayer(const LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +00001440{
1441 ValidateNumInputsOutputs(layerParam, 1, 1);
1442
1443 const string& name = layerParam.name();
1444 const ReLUParameter& param = layerParam.relu_param();
1445
1446 ActivationDescriptor activationDescriptor;
1447 const float negativeSlope = param.negative_slope();
1448 if (negativeSlope == 0.0f)
1449 {
1450 activationDescriptor.m_Function = ActivationFunction::ReLu;
1451 }
1452 else
1453 {
1454 activationDescriptor.m_Function = ActivationFunction::LeakyReLu;
1455 activationDescriptor.m_A = negativeSlope;
1456 }
1457
1458 const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo();
1459 IConnectableLayer* const activationLayer = m_Network->AddActivationLayer(activationDescriptor, name.c_str());
1460 GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).Connect(activationLayer->GetInputSlot(0));
1461 activationLayer->GetOutputSlot(0).SetTensorInfo(inputInfo);
1462 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), activationLayer->GetOutputSlot(0));
1463}
1464
Kevin Mayef33cb12021-01-29 14:24:57 +00001465void ICaffeParser::CaffeParserImpl::ParseLRNLayer(const LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +00001466{
1467 ValidateNumInputsOutputs(layerParam, 1, 1);
1468
1469 LRNParameter param = layerParam.lrn_param();
1470
1471 const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo();
1472
telsoa01c577f2c2018-08-31 09:22:23 +01001473 // Ignored BATCH NORMALIZATION Caffe Parameters.
1474 // Ignored MVN Caffe Parameters.
1475 // Ignored LRN Caffe Parameters.
telsoa014fcda012018-03-09 14:13:49 +00001476 // Engine
1477
1478 NormalizationDescriptor normalizationDescriptor;
1479 if (param.has_norm_region())
1480 {
1481 LRNParameter_NormRegion n = param.norm_region();
1482 switch (n)
1483 {
1484 case LRNParameter_NormRegion_ACROSS_CHANNELS:
1485 {
1486 normalizationDescriptor.m_NormChannelType = NormalizationAlgorithmChannel::Across;
1487 break;
1488 }
1489 case LRNParameter_NormRegion_WITHIN_CHANNEL:
1490 {
1491 normalizationDescriptor.m_NormChannelType = NormalizationAlgorithmChannel::Within;
1492 break;
1493 }
1494 default:
telsoa01c577f2c2018-08-31 09:22:23 +01001495 {
1496 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001497 fmt::format("Unknown region {} for LRN layer {} {}",
1498 n,
1499 layerParam.name(),
1500 CHECK_LOCATION().AsString()));
telsoa01c577f2c2018-08-31 09:22:23 +01001501 }
telsoa014fcda012018-03-09 14:13:49 +00001502 }
1503 }
1504 else
1505 {
telsoa01c577f2c2018-08-31 09:22:23 +01001506 // Caffe defaults to normalization across channels.
telsoa014fcda012018-03-09 14:13:49 +00001507 normalizationDescriptor.m_NormChannelType = NormalizationAlgorithmChannel::Across;
1508 }
1509
1510 normalizationDescriptor.m_NormMethodType = NormalizationAlgorithmMethod::LocalBrightness;
1511 if (param.has_local_size())
1512 {
1513 normalizationDescriptor.m_NormSize = param.local_size();
1514 }
1515 else
1516 {
telsoa01c577f2c2018-08-31 09:22:23 +01001517 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001518 fmt::format("local_size not defined for LRN layer {} {}",
1519 layerParam.name(),
1520 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001521 }
1522
1523 if (param.has_alpha())
1524 {
1525 normalizationDescriptor.m_Alpha = param.alpha();
Matthew Sloyan24ac8592020-09-23 16:57:23 +01001526 normalizationDescriptor.m_Alpha /= armnn::numeric_cast<float>(param.local_size());
telsoa014fcda012018-03-09 14:13:49 +00001527 }
1528 else
1529 {
telsoa01c577f2c2018-08-31 09:22:23 +01001530 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001531 fmt::format("Alpha not defined for LRN layer {} {}",
1532 layerParam.name(),
1533 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001534 }
1535 if (param.has_beta())
1536 {
1537 normalizationDescriptor.m_Beta = param.beta();
1538 }
1539 else
1540 {
telsoa01c577f2c2018-08-31 09:22:23 +01001541 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001542 fmt::format("Beta not defined for LRN layer {} {}",
1543 layerParam.name(),
1544 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001545 }
telsoa01c577f2c2018-08-31 09:22:23 +01001546
telsoa014fcda012018-03-09 14:13:49 +00001547 if (param.has_k())
1548 {
1549 normalizationDescriptor.m_K = param.k();
1550 }
1551 else
telsoa01c577f2c2018-08-31 09:22:23 +01001552 {
telsoa014fcda012018-03-09 14:13:49 +00001553 normalizationDescriptor.m_K = 1;
telsoa01c577f2c2018-08-31 09:22:23 +01001554 }
telsoa014fcda012018-03-09 14:13:49 +00001555
1556 IConnectableLayer* const normLayer = m_Network->AddNormalizationLayer(normalizationDescriptor,
1557 layerParam.name().c_str());
1558 GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).Connect(normLayer->GetInputSlot(0));
1559 normLayer->GetOutputSlot(0).SetTensorInfo(inputInfo);
1560
1561 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), normLayer->GetOutputSlot(0));
1562}
1563
Kevin Mayef33cb12021-01-29 14:24:57 +00001564void ICaffeParser::CaffeParserImpl::ParseInnerProductLayer(const LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +00001565{
1566 InnerProductParameter param = layerParam.inner_product_param();
1567
1568 ValidateNumInputsOutputs(layerParam, 1, 1);
1569
1570 unsigned int outputSize = param.num_output();
1571
telsoa01c577f2c2018-08-31 09:22:23 +01001572 // Ignored Caffe Parameters:
telsoa014fcda012018-03-09 14:13:49 +00001573 // Weight Filler
1574 // Bias Filler
1575 // Engine
1576 // Axis
1577
1578 FullyConnectedDescriptor tensorFullyConnectedDescriptor;
1579
1580 if (param.has_transpose())
1581 {
telsoa01c577f2c2018-08-31 09:22:23 +01001582 // If true, assumes transposed weights.
telsoa014fcda012018-03-09 14:13:49 +00001583 tensorFullyConnectedDescriptor.m_TransposeWeightMatrix = param.transpose();
1584 }
1585 else
1586 {
telsoa01c577f2c2018-08-31 09:22:23 +01001587 // Caffe defaults to transposed.
telsoa014fcda012018-03-09 14:13:49 +00001588 tensorFullyConnectedDescriptor.m_TransposeWeightMatrix = true;
1589 }
1590
1591 const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo();
1592
1593 TensorInfo weightInfo;
1594 TensorInfo biasInfo;
1595
telsoa01c577f2c2018-08-31 09:22:23 +01001596 // Allows implicit flattening of extra dimensions.
telsoa014fcda012018-03-09 14:13:49 +00001597 unsigned int inputSize = inputInfo.GetShape()[1];
1598 for (unsigned int i = 2; i < inputInfo.GetNumDimensions(); ++i)
1599 {
1600 inputSize *= inputInfo.GetShape()[i];
1601 }
1602
telsoa01c577f2c2018-08-31 09:22:23 +01001603 const float* weightDataPtr = GetArrayPtrFromBlob(layerParam, 0);
telsoa014fcda012018-03-09 14:13:49 +00001604 const unsigned int swTD[2] = { outputSize, inputSize };
telsoa01c577f2c2018-08-31 09:22:23 +01001605 ConstTensor weights(TensorInfo(2, swTD, DataType::Float32), weightDataPtr);
telsoa014fcda012018-03-09 14:13:49 +00001606
1607 tensorFullyConnectedDescriptor.m_BiasEnabled = true;
telsoa01c577f2c2018-08-31 09:22:23 +01001608 // Todo: check whether bias enabled.
telsoa014fcda012018-03-09 14:13:49 +00001609 armnn::IConnectableLayer* fullyConnectedLayer = nullptr;
1610 if (tensorFullyConnectedDescriptor.m_BiasEnabled)
1611 {
1612 // BIAS VALUE
telsoa01c577f2c2018-08-31 09:22:23 +01001613 const float* biasDataPtr = GetArrayPtrFromBlob(layerParam, 1);
telsoa014fcda012018-03-09 14:13:49 +00001614
1615 const unsigned int sbTD[1] = { outputSize };
1616
telsoa01c577f2c2018-08-31 09:22:23 +01001617 ConstTensor biases(TensorInfo(1, sbTD, DataType::Float32), biasDataPtr);
telsoa014fcda012018-03-09 14:13:49 +00001618
Matteo Martincighfc598e12019-05-14 10:36:13 +01001619 fullyConnectedLayer = m_Network->AddFullyConnectedLayer(tensorFullyConnectedDescriptor,
1620 weights,
1621 Optional<ConstTensor>(biases),
1622 layerParam.name().c_str());
telsoa014fcda012018-03-09 14:13:49 +00001623 }
1624 else
1625 {
Matteo Martincighfc598e12019-05-14 10:36:13 +01001626 fullyConnectedLayer = m_Network->AddFullyConnectedLayer(tensorFullyConnectedDescriptor,
1627 weights,
1628 EmptyOptional(),
1629 layerParam.name().c_str());
telsoa014fcda012018-03-09 14:13:49 +00001630 }
1631
1632 TensorInfo outputInfo({ inputInfo.GetShape()[0], outputSize }, DataType::Float32);
1633 GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).Connect(fullyConnectedLayer->GetInputSlot(0));
1634 fullyConnectedLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1635 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), fullyConnectedLayer->GetOutputSlot(0));
1636}
1637
Kevin Mayef33cb12021-01-29 14:24:57 +00001638void ICaffeParser::CaffeParserImpl::ParseSoftmaxLayer(const LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +00001639{
1640 ValidateNumInputsOutputs(layerParam, 1, 1);
1641
1642 SoftmaxParameter param = layerParam.softmax_param();
1643
1644 const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo();
1645
telsoa01c577f2c2018-08-31 09:22:23 +01001646 // Ignored Caffe Parameters:
telsoa014fcda012018-03-09 14:13:49 +00001647 // axis
1648 // Engine
1649
1650 armnn::SoftmaxDescriptor softmaxDescriptor;
Teresa Charlin4320c922020-08-12 16:04:41 +01001651 softmaxDescriptor.m_Axis = 1;
telsoa014fcda012018-03-09 14:13:49 +00001652 armnn::IConnectableLayer* const softmaxLayer = m_Network->AddSoftmaxLayer(
1653 softmaxDescriptor,
1654 layerParam.name().c_str());
1655 GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).Connect(softmaxLayer->GetInputSlot(0));
1656 softmaxLayer->GetOutputSlot(0).SetTensorInfo(inputInfo);
1657 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), softmaxLayer->GetOutputSlot(0));
1658}
1659
Kevin Mayef33cb12021-01-29 14:24:57 +00001660void ICaffeParser::CaffeParserImpl::ParseEltwiseLayer(const LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +00001661{
1662 ValidateNumInputsOutputs(layerParam, 2, 1);
1663
1664 const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo();
1665
telsoa01c577f2c2018-08-31 09:22:23 +01001666 // Ignored Caffe Parameters:
telsoa014fcda012018-03-09 14:13:49 +00001667 // coeff
1668
telsoa01c577f2c2018-08-31 09:22:23 +01001669 EltwiseParameter_EltwiseOp operation = EltwiseParameter_EltwiseOp_SUM; // Defaults to sum as per caffe.
telsoa014fcda012018-03-09 14:13:49 +00001670
1671 if (layerParam.has_eltwise_param() && layerParam.eltwise_param().has_operation())
1672 {
1673 operation = layerParam.eltwise_param().operation();
1674 }
1675
1676 armnn::IConnectableLayer* newLayer = nullptr;
1677 switch (operation)
1678 {
1679 case EltwiseParameter_EltwiseOp_SUM:
1680 {
1681 newLayer = m_Network->AddAdditionLayer(layerParam.name().c_str());
1682 break;
1683 }
1684 case EltwiseParameter_EltwiseOp_PROD:
1685 {
1686 newLayer = m_Network->AddMultiplicationLayer(layerParam.name().c_str());
1687 break;
1688 }
1689 default:
1690 {
telsoa01c577f2c2018-08-31 09:22:23 +01001691 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001692 fmt::format("Unsupported operation {} in Eltwise layer {} {}",
1693 operation,
1694 layerParam.name(),
1695 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001696 }
1697 }
1698
1699 GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).Connect(newLayer->GetInputSlot(0));
1700 GetArmnnOutputSlotForCaffeTop(layerParam.bottom(1)).Connect(newLayer->GetInputSlot(1));
1701 newLayer->GetOutputSlot(0).SetTensorInfo(inputInfo);
1702 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), newLayer->GetOutputSlot(0));
1703}
1704
Kevin Mayef33cb12021-01-29 14:24:57 +00001705void ICaffeParser::CaffeParserImpl::ParseConcatLayer(const LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +00001706{
1707 unsigned int numInputs = static_cast<unsigned int>(layerParam.bottom_size());
telsoa01c577f2c2018-08-31 09:22:23 +01001708 // We assume concat happens along the channel dimension, which is 1 in (0, 1, 2, 3).
telsoa014fcda012018-03-09 14:13:49 +00001709 unsigned int concatDim = 1;
1710 unsigned int numOfDims = 4;
1711
telsoa01c577f2c2018-08-31 09:22:23 +01001712 // we only consider 4-D tensor here
1713 OriginsDescriptor concatDescriptor(static_cast<uint32_t>(numInputs), numOfDims);
telsoa014fcda012018-03-09 14:13:49 +00001714 std::vector<unsigned int>mergeDimSizes(numOfDims, 0u);
1715
1716 unsigned int mergeDim = 0;
1717 for (unsigned int viewIndex = 0; viewIndex < numInputs; ++viewIndex)
1718 {
1719 const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(
Matthew Sloyan589e3e82020-09-11 16:17:48 +01001720 layerParam.bottom(armnn::numeric_cast<int>(viewIndex))).GetTensorInfo();
telsoa01c577f2c2018-08-31 09:22:23 +01001721 // Checks whether the dimensions of the input tensors are actually 4.
telsoa014fcda012018-03-09 14:13:49 +00001722 if (inputInfo.GetNumDimensions()!=4)
1723 {
telsoa01c577f2c2018-08-31 09:22:23 +01001724 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001725 fmt::format("The number of dimensions for input tensors of "
1726 "the concatenation op should be 4. Inputs of {} has "
1727 "{} dimensions. {}",
1728 layerParam.name(),
1729 inputInfo.GetNumDimensions(),
1730 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001731 }
1732
1733 mergeDimSizes[0] = inputInfo.GetShape()[0];
1734 mergeDimSizes[1] = inputInfo.GetShape()[1];
1735 mergeDimSizes[2] = inputInfo.GetShape()[2];
1736 mergeDimSizes[3] = inputInfo.GetShape()[3];
1737
1738 for (unsigned int j = 0; j < concatDim; ++j)
1739 {
1740 concatDescriptor.SetViewOriginCoord(viewIndex, j, 0);
1741 }
1742
1743 concatDescriptor.SetViewOriginCoord(viewIndex, concatDim, mergeDim);
1744 mergeDim += mergeDimSizes[concatDim];
1745
1746 for (unsigned int j = concatDim+1; j < numOfDims; ++j)
1747 {
1748 concatDescriptor.SetViewOriginCoord(viewIndex, j, 0);
1749 }
1750 }
1751 mergeDimSizes[concatDim] = mergeDim;
1752
Jim Flynn906f9462019-05-10 13:55:21 +01001753 armnn::IConnectableLayer* concatlayer = m_Network->AddConcatLayer(concatDescriptor, layerParam.name().c_str());
telsoa014fcda012018-03-09 14:13:49 +00001754 for (unsigned int i = 0; i < numInputs; ++i)
1755 {
Matthew Sloyan589e3e82020-09-11 16:17:48 +01001756 armnn::IOutputSlot& outputSlot = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(armnn::numeric_cast<int>(i)));
telsoa014fcda012018-03-09 14:13:49 +00001757 outputSlot.Connect(concatlayer->GetInputSlot(i));
1758 }
1759
1760 concatlayer->GetOutputSlot(0).SetTensorInfo(armnn::TensorInfo(numOfDims, mergeDimSizes.data(), DataType::Float32));
1761 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), concatlayer->GetOutputSlot(0));
1762}
1763
Kevin Mayef33cb12021-01-29 14:24:57 +00001764void ICaffeParser::CaffeParserImpl::ParseBatchNormLayer(const LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +00001765{
1766 ValidateNumInputsOutputs(layerParam, 1, 1);
1767
1768 const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo();
1769
1770 string name = layerParam.name();
1771
1772 BatchNormParameter param = layerParam.batch_norm_param();
1773 // If use_global_stats is not explicitly set in the model, assume it to be true (its default value
1774 // when the network is in the testing phase).
1775 if (param.has_use_global_stats())
1776 {
1777 if (!param.use_global_stats())
1778 {
telsoa01c577f2c2018-08-31 09:22:23 +01001779 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001780 fmt::format("Error parsing Batch Norm layer '{}': "
1781 "Parameter 'use_global_stats' is set to false, which is "
1782 "unsupported (value used for training). {}",
1783 name,
1784 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001785 }
1786 }
1787
1788 BatchNormalizationDescriptor desc;
1789 desc.m_Eps = param.eps();
1790
1791 unsigned int channels = inputInfo.GetShape()[1];
1792 unsigned int shape[] = {channels};
1793
1794 vector<float> meanData(channels);
1795 GetDataFromBlob(layerParam, meanData, 0);
1796
1797 vector<float> varianceData(channels);
1798 GetDataFromBlob(layerParam, varianceData, 1);
1799
telsoa01c577f2c2018-08-31 09:22:23 +01001800 // Reads moving average factor and applies scaling (if required).
Matthew Sloyan589e3e82020-09-11 16:17:48 +01001801 const BlobProto& blob = layerParam.blobs(armnn::numeric_cast<int>(2));
1802 const float movingAverageFactor = blob.data(armnn::numeric_cast<int>(0));
surmeh013537c2c2018-05-18 16:31:43 +01001803 if(movingAverageFactor != 0.0f)
1804 {
1805 const float scaleFactor = 1.0f / movingAverageFactor;
1806 auto scaleFunction = [scaleFactor](float f) -> float { return f * scaleFactor; };
1807
1808 std::transform(varianceData.begin(), varianceData.end(), varianceData.begin(), scaleFunction);
1809 std::transform(meanData.begin(), meanData.end(), meanData.begin(), scaleFunction);
1810 }
1811
telsoa01c577f2c2018-08-31 09:22:23 +01001812 // Identifies scale operation.
telsoa014fcda012018-03-09 14:13:49 +00001813 vector<float> betaData(channels, 0.0f);
1814 vector<float> gammaData(channels, 1.0f);
1815
1816 ConstTensor mean(TensorInfo(1, shape, armnn::DataType::Float32), meanData);
1817 ConstTensor variance(TensorInfo(1, shape, armnn::DataType::Float32), varianceData);
1818 ConstTensor beta(TensorInfo(1, shape, armnn::DataType::Float32), betaData);
1819 ConstTensor gamma(TensorInfo(1, shape, armnn::DataType::Float32), gammaData);
1820
1821 armnn::IConnectableLayer* const batchNormLayer = m_Network->AddBatchNormalizationLayer(desc,
1822 mean, variance, beta, gamma, name.c_str());
1823 GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).Connect(batchNormLayer->GetInputSlot(0));
1824 batchNormLayer->GetOutputSlot(0).SetTensorInfo(inputInfo);
1825 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), batchNormLayer->GetOutputSlot(0));
1826}
1827
Kevin Mayef33cb12021-01-29 14:24:57 +00001828void ICaffeParser::CaffeParserImpl::ParseScaleLayer(const LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +00001829{
telsoa01c577f2c2018-08-31 09:22:23 +01001830 // Current unoptimal solution: add a batchnormalization layer with 0 mean and 1 variance.
telsoa014fcda012018-03-09 14:13:49 +00001831 ValidateNumInputsOutputs(layerParam, 1, 1);
1832
1833 const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo();
1834
1835 string name = layerParam.name();
1836
1837 ScaleParameter param = layerParam.scale_param();
1838 if (param.axis() != 1)
1839 {
1840 // Would have to use something other than BatchNormalizationLayer in this case
telsoa01c577f2c2018-08-31 09:22:23 +01001841 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001842 fmt::format("Loading Scale Layer: Only axis 1 is supported currently. "
1843 "Layer={} Axis={} {}",
1844 layerParam.name(),
1845 param.axis(),
1846 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001847 }
1848
1849 unsigned int channels = inputInfo.GetShape()[1];
1850 unsigned int shape[] = {channels};
1851
1852 BatchNormalizationDescriptor desc;
telsoa01c577f2c2018-08-31 09:22:23 +01001853 desc.m_Eps = 0.0f; // Don't need epsilon if variance is 1.
telsoa014fcda012018-03-09 14:13:49 +00001854 vector<float> meanData(channels, 0.0f);
1855 vector<float> varianceData(channels, 1.0f);
1856 vector<float> betaData(channels, 0.0f);
1857 vector<float> gammaData(channels);
1858
1859 GetDataFromBlob(layerParam, gammaData, 0);
1860
1861 if(param.has_bias_term())
1862 {
1863 GetDataFromBlob(layerParam, betaData, 1);
1864 }
1865
1866 ConstTensor mean(TensorInfo(1, shape, armnn::DataType::Float32), meanData);
1867 ConstTensor variance(TensorInfo(1, shape, armnn::DataType::Float32), varianceData);
1868 ConstTensor beta(TensorInfo(1, shape, armnn::DataType::Float32), betaData);
1869 ConstTensor gamma(TensorInfo(1, shape, armnn::DataType::Float32), gammaData);
1870
1871 armnn::IConnectableLayer* const batchNormLayer = m_Network->AddBatchNormalizationLayer(desc,
1872 mean, variance, beta, gamma, name.c_str());
1873 GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).Connect(batchNormLayer->GetInputSlot(0));
1874 batchNormLayer->GetOutputSlot(0).SetTensorInfo(inputInfo);
1875 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), batchNormLayer->GetOutputSlot(0));
1876}
1877
Kevin Mayef33cb12021-01-29 14:24:57 +00001878void ICaffeParser::CaffeParserImpl::ParseSplitLayer(const caffe::LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +00001879{
telsoa01c577f2c2018-08-31 09:22:23 +01001880 // Used in caffe to duplicate memory - not necessary in armnn.
telsoa014fcda012018-03-09 14:13:49 +00001881 if (layerParam.bottom_size() != 1)
1882 {
telsoa01c577f2c2018-08-31 09:22:23 +01001883 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001884 fmt::format("Split layer '{}' should have exactly 1 bottom. "
1885 "#bottoms={} {}",
1886 layerParam.name(),
1887 layerParam.bottom_size(),
1888 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001889 }
1890 armnn::IOutputSlot& outputSlot = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0));
1891 for (int i = 0; i < layerParam.top_size(); i++)
1892 {
1893 SetArmnnOutputSlotForCaffeTop(layerParam.top(i), outputSlot);
1894 }
1895}
1896
Kevin Mayef33cb12021-01-29 14:24:57 +00001897void ICaffeParser::CaffeParserImpl::ParseDropoutLayer(const caffe::LayerParameter& layerParam)
telsoa014fcda012018-03-09 14:13:49 +00001898{
telsoa01c577f2c2018-08-31 09:22:23 +01001899 // Ignored for inference, so patch the single input to its single output.
telsoa014fcda012018-03-09 14:13:49 +00001900 if (layerParam.bottom_size() != 1 || layerParam.top_size() != 1)
1901 {
telsoa01c577f2c2018-08-31 09:22:23 +01001902 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001903 fmt::format("Dropout layer '{}' should have exactly 1 bottom and 1 top. "
1904 "#bottoms={} #tops={} {}",
1905 layerParam.name(),
1906 layerParam.bottom_size(),
1907 layerParam.top_size(),
1908 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001909 }
1910 SetArmnnOutputSlotForCaffeTop(layerParam.top(0), GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)));
1911}
1912
Kevin Mayef33cb12021-01-29 14:24:57 +00001913void ICaffeParser::CaffeParserImpl::TrackInputBinding(armnn::IConnectableLayer* layer,
telsoa014fcda012018-03-09 14:13:49 +00001914 armnn::LayerBindingId id,
1915 const armnn::TensorInfo& tensorInfo)
1916{
1917 return TrackBindingPoint(layer, id, tensorInfo, layer->GetName(), m_NetworkInputsBindingInfo);
1918}
1919
Kevin Mayef33cb12021-01-29 14:24:57 +00001920void ICaffeParser::CaffeParserImpl::TrackOutputBinding(armnn::IConnectableLayer* layer,
telsoa014fcda012018-03-09 14:13:49 +00001921 armnn::LayerBindingId id,
1922 const armnn::TensorInfo& tensorInfo)
1923{
1924 return TrackBindingPoint(layer, id, tensorInfo, layer->GetName(), m_NetworkOutputsBindingInfo);
1925}
1926
Kevin Mayef33cb12021-01-29 14:24:57 +00001927void ICaffeParser::CaffeParserImpl::TrackBindingPoint(armnn::IConnectableLayer* layer,
telsoa014fcda012018-03-09 14:13:49 +00001928 armnn::LayerBindingId id,
1929 const armnn::TensorInfo& tensorInfo,
1930 const char* bindingPointDesc,
1931 std::unordered_map<std::string, BindingPointInfo>& nameToBindingInfo)
1932{
1933 const std::string layerName = layer->GetName();
1934 auto it = nameToBindingInfo.find(layerName);
1935 if (it == nameToBindingInfo.end())
1936 {
1937 nameToBindingInfo[layerName] = std::make_pair(id, tensorInfo);
1938 }
1939 else
1940 {
telsoa01c577f2c2018-08-31 09:22:23 +01001941 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001942 fmt::format("Id {} used by more than one {} layer {}",
1943 id,
1944 bindingPointDesc,
1945 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001946 }
1947}
1948
Kevin Mayef33cb12021-01-29 14:24:57 +00001949armnn::IOutputSlot& ICaffeParser::CaffeParserImpl::GetArmnnOutputSlotForCaffeTop(const std::string& caffeTopName) const
telsoa014fcda012018-03-09 14:13:49 +00001950{
1951 auto it = m_ArmnnOutputSlotForCaffeTop.find(caffeTopName);
1952 if (it != m_ArmnnOutputSlotForCaffeTop.end())
1953 {
1954 return *it->second;
1955 }
1956 else
1957 {
telsoa01c577f2c2018-08-31 09:22:23 +01001958 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001959 fmt::format("Could not find armnn output slot for Caffe top '{}' {}",
1960 caffeTopName,
1961 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001962 }
1963}
1964
Kevin Mayef33cb12021-01-29 14:24:57 +00001965void ICaffeParser::CaffeParserImpl::SetArmnnOutputSlotForCaffeTop(
telsoa01c577f2c2018-08-31 09:22:23 +01001966 const std::string& caffeTopName, armnn::IOutputSlot& armnnOutputSlot)
telsoa014fcda012018-03-09 14:13:49 +00001967{
1968 auto it = m_ArmnnOutputSlotForCaffeTop.find(caffeTopName);
1969 if (it == m_ArmnnOutputSlotForCaffeTop.end())
1970 {
1971 m_ArmnnOutputSlotForCaffeTop[caffeTopName] = &armnnOutputSlot;
1972 }
1973 else
1974 {
telsoa01c577f2c2018-08-31 09:22:23 +01001975 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001976 fmt::format("Attempting to add duplicate entry for Caffe top '{}' {}",
1977 caffeTopName,
1978 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00001979 }
1980}
1981
telsoa01c577f2c2018-08-31 09:22:23 +01001982// Note: can move to CaffeParser when/if we optimise the text/string format
1983// to load on a layer by layer basis
Kevin Mayef33cb12021-01-29 14:24:57 +00001984void ICaffeParser::CaffeParserImpl::ResolveInPlaceLayers(caffe::NetParameter& netParameter)
telsoa014fcda012018-03-09 14:13:49 +00001985{
telsoa01c577f2c2018-08-31 09:22:23 +01001986 // Finds layers with the same top.
telsoa014fcda012018-03-09 14:13:49 +00001987 std::map<std::string, std::vector<caffe::LayerParameter*>> layersByTop;
1988 for (int layerIdx = 0; layerIdx < netParameter.layer_size(); ++layerIdx)
1989 {
1990 caffe::LayerParameter& layer = *netParameter.mutable_layer(layerIdx);
telsoa01c577f2c2018-08-31 09:22:23 +01001991 std::string name = layer.name();
telsoa014fcda012018-03-09 14:13:49 +00001992 for (int i = 0; i < layer.top_size(); ++i)
1993 {
1994 layersByTop[layer.top(i)].push_back(&layer);
1995 }
1996 }
1997
telsoa01c577f2c2018-08-31 09:22:23 +01001998 // 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 +00001999 // Note that for 'regular' layers, there will be a single layer in each group and so this will be a no-op.
2000 for (auto layersWithSameTopIt : layersByTop)
2001 {
2002 const std::string& top = layersWithSameTopIt.first;
2003 const std::vector<caffe::LayerParameter*>& layersWithSameTop = layersWithSameTopIt.second;
2004
telsoa01c577f2c2018-08-31 09:22:23 +01002005 // Chains the layers together in the order that they are listed in the prototxt (hopefully this is correct).
telsoa014fcda012018-03-09 14:13:49 +00002006 // Note that the last layer will not have its top modified so that other layers will continue to reference it.
2007 for (unsigned int layerIdx = 0; layerIdx < layersWithSameTop.size() - 1; ++layerIdx)
2008 {
2009 caffe::LayerParameter& layer1 = *layersWithSameTop[layerIdx];
2010 caffe::LayerParameter& layer2 = *layersWithSameTop[layerIdx+1];
2011 if (layer1.top_size() != 1)
2012 {
telsoa01c577f2c2018-08-31 09:22:23 +01002013 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002014 fmt::format("Node '{}' is an in-place layer but doesn't have exactly one "
2015 "top. It has {} instead. {}",
2016 layer1.name(),
2017 layer1.top_size(),
2018 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00002019 }
2020 std::string newTop = layer1.name() + "_top";
2021 layer1.set_top(0, newTop);
2022 if (layer2.bottom_size() != 1 || layer2.bottom(0) != top)
2023 {
telsoa01c577f2c2018-08-31 09:22:23 +01002024 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002025 fmt::format("Node '{}' is an in-place layer but "
2026 "doesn't have exactly one bottom, or it doesn't match its top. "
2027 "#bottoms={}, first bottom is {}, top is {} {}",
2028 layer2.name(),
2029 layer2.bottom(0),
2030 top,
2031 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00002032 }
2033 layer2.set_bottom(0, newTop);
2034 }
2035 }
2036}
2037
telsoa01c577f2c2018-08-31 09:22:23 +01002038// Note: can move to CaffeParser when/if we optimise the text/string format
2039// to load on a layer by layer basis
Kevin Mayef33cb12021-01-29 14:24:57 +00002040void ICaffeParser::CaffeParserImpl::LoadNetParam(NetParameter& netParameter)
telsoa014fcda012018-03-09 14:13:49 +00002041{
telsoa01c577f2c2018-08-31 09:22:23 +01002042 // Caffe models sometimes have an implicit input layer.
2043 // In that case, add an explicit one.
telsoa014fcda012018-03-09 14:13:49 +00002044 if (netParameter.input_size() > 0)
2045 {
2046 LayerParameter* newLayer = netParameter.add_layer();
2047
2048 newLayer->set_type("Input");
2049 newLayer->set_name(netParameter.input(0));
2050 newLayer->add_top(netParameter.input(0));
2051
2052 InputParameter* inputParam = newLayer->mutable_input_param();
2053 BlobShape* shape = inputParam->add_shape();
2054
2055 int dim_size = netParameter.input_dim_size();
2056 for (int i = 0; i < dim_size; ++i)
2057 {
2058 shape->add_dim(netParameter.input_dim(i));
2059 }
2060 }
2061
telsoa01c577f2c2018-08-31 09:22:23 +01002062 // Replaces in-place layers with regular ones to make the rest of the parsing easier.
telsoa014fcda012018-03-09 14:13:49 +00002063 ResolveInPlaceLayers(netParameter);
2064
telsoa01c577f2c2018-08-31 09:22:23 +01002065 // Creates a lookup of Caffe layers by name.
telsoa014fcda012018-03-09 14:13:49 +00002066 for (int i = 0; i < netParameter.layer_size(); ++i)
2067 {
2068 const caffe::LayerParameter& layer = netParameter.layer(i);
2069 for (int i = 0; i < layer.top_size(); ++i)
2070 {
2071 m_CaffeLayersByTopName[layer.top(i)] = &layer;
2072 }
2073 }
2074
telsoa01c577f2c2018-08-31 09:22:23 +01002075 // Finds the output layers the user requested.
telsoa014fcda012018-03-09 14:13:49 +00002076 std::vector<const caffe::LayerParameter*> targetLayers;
2077 for (const std::string& requestedOutputName : m_RequestedOutputs)
2078 {
2079 auto nodeIt = m_CaffeLayersByTopName.find(requestedOutputName);
2080 if (nodeIt == m_CaffeLayersByTopName.end())
2081 {
telsoa01c577f2c2018-08-31 09:22:23 +01002082 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002083 fmt::format("Couldn't find requested output layer '{}' in graph {}",
2084 requestedOutputName,
2085 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00002086 }
2087 targetLayers.push_back(nodeIt->second);
2088 }
2089
telsoa01c577f2c2018-08-31 09:22:23 +01002090 // Sorts them into a linear ordering such that all inputs of a node are before the node itself.
telsoa014fcda012018-03-09 14:13:49 +00002091 std::vector<const caffe::LayerParameter*> sortedNodes;
2092 if (!armnnUtils::GraphTopologicalSort<const caffe::LayerParameter*>(
2093 targetLayers,
2094 [this](const caffe::LayerParameter* node)
2095 {
2096 return GetInputs(*node);
2097 },
2098 sortedNodes))
2099 {
telsoa01c577f2c2018-08-31 09:22:23 +01002100 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002101 fmt::format("Cycle detected in graph. #nodes: {} {}",
2102 sortedNodes.size(),
2103 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00002104 }
2105
telsoa01c577f2c2018-08-31 09:22:23 +01002106 // 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 +00002107 for (const caffe::LayerParameter* current : sortedNodes)
2108 {
2109 auto it = ms_CaffeLayerNameToParsingFunctions.find(current->type());
2110 if (it == ms_CaffeLayerNameToParsingFunctions.end())
2111 {
telsoa01c577f2c2018-08-31 09:22:23 +01002112 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002113 fmt::format("Unsupported layer type: '{}' for layer {} {}",
2114 current->type(),
2115 current->name(),
2116 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00002117 }
2118 auto func = it->second;
2119 (this->*func)(*current);
2120 }
2121
telsoa01c577f2c2018-08-31 09:22:23 +01002122 // Adds ArmNN output layers connected to each requested output.
telsoa014fcda012018-03-09 14:13:49 +00002123 for (const std::string& requestedOutput : m_RequestedOutputs)
2124 {
2125 armnn::IOutputSlot& outputSlot = GetArmnnOutputSlotForCaffeTop(requestedOutput);
2126
Matthew Sloyan589e3e82020-09-11 16:17:48 +01002127 const armnn::LayerBindingId outputId = armnn::numeric_cast<armnn::LayerBindingId>(
telsoa014fcda012018-03-09 14:13:49 +00002128 m_NetworkOutputsBindingInfo.size());
2129 armnn::IConnectableLayer* const outputLayer = m_Network->AddOutputLayer(outputId, requestedOutput.c_str());
2130 outputSlot.Connect(outputLayer->GetInputSlot(0));
2131
2132 TrackOutputBinding(outputLayer, outputId, outputLayer->GetInputSlot(0).GetConnection()->GetTensorInfo());
2133 }
2134}
2135
Kevin Mayef33cb12021-01-29 14:24:57 +00002136INetworkPtr ICaffeParser::CaffeParserImpl::CreateNetworkFromTextFile(const char* graphFile,
telsoa014fcda012018-03-09 14:13:49 +00002137 const std::map<std::string, armnn::TensorShape>& inputShapes,
2138 const std::vector<std::string>& requestedOutputs)
2139{
2140 FILE* fd = fopen(graphFile, "r");
2141
2142 if (fd == nullptr)
2143 {
telsoa01c577f2c2018-08-31 09:22:23 +01002144 throw FileNotFoundException(
James Ward58dec6b2020-09-11 17:32:44 +01002145 fmt::format("Failed to open graph file: {} {}",
2146 graphFile,
2147 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00002148 }
2149
telsoa01c577f2c2018-08-31 09:22:23 +01002150 // Parses the file into a message.
telsoa014fcda012018-03-09 14:13:49 +00002151 NetParameter netParam;
2152 auto input = new google::protobuf::io::FileInputStream(fileno(fd));
2153 bool success = google::protobuf::TextFormat::Parse(input, &netParam);
2154 delete input;
2155 fclose(fd);
2156
2157 if (!success)
2158 {
telsoa01c577f2c2018-08-31 09:22:23 +01002159 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002160 fmt::format("Failed to parse graph file: {} {}",
2161 graphFile,
2162 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00002163 }
2164
2165 return CreateNetworkFromNetParameter(netParam, inputShapes, requestedOutputs);
2166}
2167
Kevin Mayef33cb12021-01-29 14:24:57 +00002168INetworkPtr ICaffeParser::CaffeParserImpl::CreateNetworkFromString(const char* protoText,
telsoa014fcda012018-03-09 14:13:49 +00002169 const std::map<std::string, armnn::TensorShape>& inputShapes,
2170 const std::vector<std::string>& requestedOutputs)
2171{
telsoa01c577f2c2018-08-31 09:22:23 +01002172 // Parses the string into a message.
telsoa014fcda012018-03-09 14:13:49 +00002173 NetParameter netParam;
2174 bool success = google::protobuf::TextFormat::ParseFromString(protoText, &netParam);
2175
2176 if (!success)
2177 {
telsoa01c577f2c2018-08-31 09:22:23 +01002178 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002179 fmt::format("Failed to parse graph string {}",
2180 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00002181 }
2182
2183 return CreateNetworkFromNetParameter(netParam, inputShapes, requestedOutputs);
2184}
2185
2186INetworkPtr CaffeParser::CreateNetworkFromBinaryFile(const char* graphFile,
2187 const std::map<std::string, armnn::TensorShape>& inputShapes,
2188 const std::vector<std::string>& requestedOutputs)
2189{
2190 FILE* fd = fopen(graphFile, "rb");
2191
2192 if (fd == nullptr)
2193 {
telsoa01c577f2c2018-08-31 09:22:23 +01002194 throw FileNotFoundException(
James Ward58dec6b2020-09-11 17:32:44 +01002195 fmt::format("Failed to open graph file at: {} {}",
2196 graphFile,
2197 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00002198 }
2199
telsoa01c577f2c2018-08-31 09:22:23 +01002200 // Parses the file into a message.
telsoa014fcda012018-03-09 14:13:49 +00002201 NetParameter netParam;
2202
2203 FileInputStream inStream(fileno(fd));
2204 CodedInputStream codedStream(&inStream);
Nikhil Raje5181532020-10-09 14:52:25 +01002205 codedStream.SetTotalBytesLimit(INT_MAX);
telsoa014fcda012018-03-09 14:13:49 +00002206 bool success = netParam.ParseFromCodedStream(&codedStream);
2207 fclose(fd);
2208
2209 if (!success)
2210 {
telsoa01c577f2c2018-08-31 09:22:23 +01002211 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002212 fmt::format("Failed to parse protobuf file: {} {}",
2213 graphFile,
2214 CHECK_LOCATION().AsString()));
telsoa014fcda012018-03-09 14:13:49 +00002215 }
2216
2217 return CreateNetworkFromNetParameter(netParam, inputShapes, requestedOutputs);
2218}
2219
telsoa01c577f2c2018-08-31 09:22:23 +01002220// Note: can move to CaffeParser when/if we optimise the text/string format
2221// to load on a layer by layer basis
Kevin Mayef33cb12021-01-29 14:24:57 +00002222INetworkPtr ICaffeParser::CaffeParserImpl::CreateNetworkFromNetParameter(NetParameter& netParam,
telsoa014fcda012018-03-09 14:13:49 +00002223 const std::map<std::string, armnn::TensorShape>& inputShapes,
2224 const std::vector<std::string>& requestedOutputs)
2225{
2226 m_NetworkInputsBindingInfo.clear();
2227 m_NetworkOutputsBindingInfo.clear();
2228
2229 m_Network = INetwork::Create();
2230
2231 m_InputShapes = inputShapes;
2232 if (requestedOutputs.size() == 0)
2233 {
2234 throw ParseException("requestedOutputs must have at least one entry");
2235 }
2236 m_RequestedOutputs = requestedOutputs;
2237
2238 try
2239 {
2240 LoadNetParam(netParam);
2241 }
2242 catch (const ParseException& e)
2243 {
2244 Cleanup();
2245 throw e;
2246 }
2247
2248 Cleanup();
2249
2250 return move(m_Network);
2251}
2252
Kevin Mayef33cb12021-01-29 14:24:57 +00002253void ICaffeParser::CaffeParserImpl::Cleanup() {
telsoa014fcda012018-03-09 14:13:49 +00002254 // cleanup, in case we reuse this parser
telsoa014fcda012018-03-09 14:13:49 +00002255 m_InputShapes.clear();
2256 m_RequestedOutputs.clear();
2257 m_ArmnnOutputSlotForCaffeTop.clear();
telsoa01c577f2c2018-08-31 09:22:23 +01002258 // NOTE: when we get the text/string format
2259 // optimised for memory then this data structure can
2260 // also move to the CaffeParser class
2261 m_CaffeLayersByTopName.clear();
telsoa014fcda012018-03-09 14:13:49 +00002262}
2263
2264}