Line data Source code
1 : // SPDX-License-Identifier: Apache-2.0
2 : /**
3 : * Copyright (C) 2021 Jihoon Lee <jhoon.it.lee@samsung.com>
4 : *
5 : * @file node_exporter.h
6 : * @date 09 April 2021
7 : * @brief NNTrainer Node exporter
8 : * @see https://github.com/nnstreamer/nntrainer
9 : * @author Jihoon Lee <jhoon.it.lee@samsung.com>
10 : * @author Donghak Park <donghak.park@samsung.com>
11 : * @bug No known bugs except for NYI items
12 : */
13 : #ifndef __NODE_EXPORTER_H__
14 : #define __NODE_EXPORTER_H__
15 :
16 : #include <memory>
17 : #include <regex>
18 : #include <string>
19 : #include <tuple>
20 : #include <utility>
21 : #include <vector>
22 :
23 : #include <base_properties.h>
24 : #include <common.h>
25 : #include <common_properties.h>
26 : #include <layer.h>
27 : #include <nntrainer_error.h>
28 : #include <util_func.h>
29 :
30 : #ifdef ENABLE_TFLITE_INTERPRETER
31 : #include <flatbuffers/flatbuffers.h>
32 : #endif
33 :
34 : namespace nntrainer {
35 :
36 : /**
37 : * @brief Forward declaration of TfOpNode
38 : *
39 : */
40 : class TfOpNode;
41 :
42 : namespace {
43 :
44 : /**
45 : * @brief meta function that return return_type when a method is being called
46 : *
47 : * @tparam method returned when certain method is being called
48 : */
49 : template <ml::train::ExportMethods method> struct return_type {
50 : using type = void;
51 : };
52 :
53 : /**
54 : * @brief meta function to check return type when the method is string vector
55 : *
56 : * @tparam specialized so not given
57 : */
58 : template <> struct return_type<ml::train::ExportMethods::METHOD_STRINGVECTOR> {
59 : using type = std::vector<std::pair<std::string, std::string>>;
60 : };
61 :
62 : /**
63 : * @brief meta function to check return type when the method is string vector
64 : *
65 : * @tparam specialized so not given
66 : */
67 : template <> struct return_type<ml::train::ExportMethods::METHOD_TFLITE> {
68 : using type = TfOpNode;
69 : };
70 :
71 : /**
72 : * @brief Create a empty ptr if null
73 : *
74 : * @tparam T type to create
75 : * @param[in/out] ptr ptr to create
76 : */
77 6869 : template <typename T> void createIfNull(std::unique_ptr<T> &ptr) {
78 6869 : if (ptr == nullptr) {
79 3050 : ptr = std::make_unique<T>();
80 : }
81 6869 : }
82 :
83 : } // namespace
84 :
85 : /**
86 : * @brief Exporter class helps to exports the node information in a predefined
87 : * way. because each method will require complete different methods, this class
88 : * exploits visitor pattern to make a custom defined saving method
89 : *
90 : */
91 : class Exporter {
92 : public:
93 : /**
94 : * @brief Construct a new Exporter object
95 : *
96 : */
97 : Exporter();
98 :
99 : #ifdef ENABLE_TFLITE_INTERPRETER
100 : /**
101 : * @brief Construct a new Exporter object with flatbuffer builder
102 : *
103 : */
104 : Exporter(flatbuffers::FlatBufferBuilder *fbb);
105 :
106 : flatbuffers::FlatBufferBuilder *getFlatbufferBuilder() { return fbb; }
107 : #endif
108 :
109 : /**
110 : * @brief Destroy the Exporter object
111 : *
112 : */
113 : ~Exporter();
114 :
115 : /**
116 : * @brief this function iterates over the property and process the property in
117 : * a designated way.
118 : *
119 : * @tparam Ts type of elements
120 : * @tparam NodeType NodeType element
121 : * @param props tuple that contains properties
122 : * @param method method to export
123 : * @param self this pointer to the layer which is being exported
124 : */
125 : template <typename... Ts, typename NodeType = void>
126 6878 : void saveResult(const std::tuple<Ts...> &props,
127 : ml::train::ExportMethods method,
128 : const NodeType *self = nullptr) {
129 6878 : switch (method) {
130 6825 : case ml::train::ExportMethods::METHOD_STRINGVECTOR: {
131 6825 : createIfNull(stored_result);
132 :
133 : /**
134 : * @brief function to pass to the iterate_prop, this saves the property
135 : * to stored_result
136 : *
137 : * @param prop property property to pass
138 : * @param index index of the current property
139 : */
140 82018 : auto callable = [this](auto &&prop, size_t index) {
141 46894 : if (!prop.empty()) {
142 28299 : std::string key = getPropKey(prop);
143 82053 : stored_result->emplace_back(std::move(key), to_string(prop));
144 : }
145 : };
146 2423 : iterate_prop(callable, props);
147 4772 : } break;
148 52 : case ml::train::ExportMethods::METHOD_TFLITE:
149 52 : saveTflResult(props, self);
150 52 : break;
151 1 : case ml::train::ExportMethods::METHOD_UNDEFINED:
152 : /// fall through intended
153 : default:
154 2 : throw exception::not_supported("given method is not supported yet");
155 : }
156 :
157 6877 : is_exported = true;
158 6877 : }
159 :
160 : /**
161 : * @brief Get the result object
162 : * @note @a unique_ptr here will be a member of @a this. The ownership is
163 : * passed to the caller, thus after getResult, the target member is cleared.
164 : * This is intended design choice to mimic single function call, between @a
165 : * saveResult and @a getResult
166 : *
167 : * @tparam methods method to get
168 : * @tparam T appropriate return type regarding the export method
169 : * @return std::unique_ptr<T> predefined return type according to the method.
170 : * @retval nullptr not exported
171 : */
172 : template <ml::train::ExportMethods methods,
173 : typename T = typename return_type<methods>::type>
174 : std::unique_ptr<T> getResult();
175 :
176 : private:
177 : /**
178 : * @brief ProtoType to enable saving result to tfnode
179 : *
180 : * @tparam PropsType propsType PropsType to receive with self
181 : * @tparam NodeType NodeType of current layer.
182 : * @param props properties to get with @a self
183 : * @param self @a this of the current layer
184 : */
185 : template <typename PropsType, typename NodeType>
186 : void saveTflResult(const PropsType &props, const NodeType *self);
187 :
188 : #ifdef ENABLE_TFLITE_INTERPRETER
189 : std::unique_ptr<TfOpNode> tf_node; /**< created node from the export */
190 : flatbuffers::FlatBufferBuilder *fbb; /**< flatbuffer builder */
191 : #endif
192 :
193 : std::unique_ptr<std::vector<std::pair<std::string, std::string>>>
194 : stored_result; /**< stored result */
195 :
196 : /// consider changing this to a promise / future if there is a async function
197 : /// involved to `saveResult`
198 : bool is_exported; /**< boolean to check if exported */
199 : };
200 :
201 : /**
202 : * @brief ProtoType to enable saving result to tfnode
203 : *
204 : * @tparam PropsType propsType PropsType to receive with self
205 : * @tparam NodeType NodeType of current layer.
206 : * @param props properties to get with @a self
207 : * @param self @a this of the current layer
208 : */
209 : template <typename PropsType, typename NodeType>
210 0 : void Exporter::saveTflResult(const PropsType &props, const NodeType *self) {
211 0 : NNTR_THROW_IF(true, nntrainer::exception::not_supported)
212 : << "given node cannot be converted to tfnode, type: "
213 : << typeid(self).name();
214 : }
215 :
216 : #ifdef ENABLE_TFLITE_INTERPRETER
217 : namespace props {
218 : class Name;
219 : class Unit;
220 : class Flatten;
221 : class Distribute;
222 : class Trainable;
223 : class InputShape;
224 : class WeightRegularizer;
225 : class WeightRegularizerConstant;
226 : class WeightInitializer;
227 : class WeightDecay;
228 : class BiasDecay;
229 : class BiasInitializer;
230 : class SharedFrom;
231 : class InputConnection;
232 : class ClipGradByGlobalNorm;
233 : class DisableBias;
234 : class Activation;
235 : class BatchNormalization;
236 : class Packed;
237 : class LossScaleForMixed;
238 : class InPlaceProp;
239 : class InPlaceDirectionProp;
240 : class Exponent;
241 : class StartIndex;
242 : class EndIndex;
243 : } // namespace props
244 :
245 : class LayerNode;
246 : /**
247 : * @copydoc template <typename PropsType, typename NodeType> void
248 : * Exporter::saveTflResult(const PropsType &props, const NodeType *self);
249 : */
250 : template <>
251 : void Exporter::saveTflResult(
252 : const std::tuple<
253 : props::Name, props::Distribute, props::Trainable,
254 : std::vector<props::InputConnection>, std::vector<props::InputShape>,
255 : props::SharedFrom, props::ClipGradByGlobalNorm, props::Packed,
256 : props::WeightDtype, props::LossScaleForMixed, props::ComputeEngine> &props,
257 : const LayerNode *self);
258 :
259 : class BatchNormalizationLayer;
260 : /**
261 : * @copydoc template <typename PropsType, typename NodeType> void
262 : * Exporter::saveTflResult(const PropsType &props, const NodeType *self);
263 : */
264 : template <>
265 : void Exporter::saveTflResult(
266 : const std::tuple<props::Epsilon, props::MuInitializer, props::VarInitializer,
267 : props::BetaInitializer, props::GammaInitializer,
268 : props::Momentum, props::Axis, props::WeightDecay,
269 : props::BiasDecay> &props,
270 : const BatchNormalizationLayer *self);
271 :
272 : class LayerImpl;
273 :
274 : /**
275 : * @copydoc template <typename PropsType, typename NodeType> void
276 : * Exporter::saveTflResult(const PropsType &props, const NodeType *self);
277 : */
278 : template <>
279 : void Exporter::saveTflResult(
280 : const std::tuple<props::WeightRegularizer, props::WeightRegularizerConstant,
281 : props::WeightInitializer, props::WeightDecay,
282 : props::BiasDecay, props::BiasInitializer, props::DisableBias,
283 : props::Print> &props,
284 : const LayerImpl *self);
285 :
286 : class FullyConnectedLayer;
287 : /**
288 : * @copydoc template <typename PropsType, typename NodeType> void
289 : * Exporter::saveTflResult(const PropsType &props, const NodeType *self);
290 : */
291 : template <>
292 : void Exporter::saveTflResult(
293 : const std::tuple<props::Unit, props::LoraRank, props::LoraAlpha> &props,
294 : const FullyConnectedLayer *self);
295 :
296 : class ActivationLayer;
297 : /**
298 : * @copydoc template <typename PropsType, typename NodeType> void
299 : * Exporter::saveTflResult(const PropsType &props, const NodeType *self);
300 : */
301 : template <>
302 : void Exporter::saveTflResult(const std::tuple<props::Activation> &props,
303 : const ActivationLayer *self);
304 :
305 : class Conv2DLayer;
306 : /**
307 : * @copydoc template <typename PropsType, typename NodeType> void
308 : * Exporter::saveTflResult(const PropsType &props, const NodeType *self);
309 : */
310 : template <>
311 : void Exporter::saveTflResult(
312 : const std::tuple<props::FilterSize, std::array<props::KernelSize, 2>,
313 : std::array<props::Stride, 2>, props::Padding2D,
314 : std::array<props::Dilation, 2>> &props,
315 : const Conv2DLayer *self);
316 :
317 : class InputLayer;
318 : /**
319 : * @copydoc template <typename PropsType, typename NodeType> void
320 : * Exporter::saveTflResult(const PropsType &props, const NodeType *self);
321 : */
322 : template <>
323 : void Exporter::saveTflResult(
324 : const std::tuple<props::Normalization, props::Standardization> &props,
325 : const InputLayer *self);
326 :
327 : class Pooling2DLayer;
328 : /**
329 : * @copydoc template <typename PropsType, typename NodeType> void
330 : * Exporter::saveTflResult(const PropsType &props, const NodeType *self);
331 : */
332 : template <>
333 : void Exporter::saveTflResult(
334 : const std::tuple<props::PoolingType, std::vector<props::PoolSize>,
335 : std::array<props::Stride, 2>, props::Padding2D> &props,
336 : const Pooling2DLayer *self);
337 :
338 : class ReshapeLayer;
339 : /**
340 : * @copydoc template <typename PropsType, typename NodeType> void
341 : * Exporter::saveTflResult(const PropsType &props, const NodeType *self);
342 : */
343 : template <>
344 : void Exporter::saveTflResult(const std::tuple<props::TargetShape> &props,
345 : const ReshapeLayer *self);
346 :
347 : class FlattenLayer;
348 : /**
349 : * @copydoc template <typename PropsType, typename NodeType> void
350 : * Exporter::saveTflResult(const PropsType &props, const NodeType *self);
351 : */
352 : template <>
353 : void Exporter::saveTflResult(const std::tuple<props::TargetShape> &props,
354 : const FlattenLayer *self);
355 :
356 : class AdditionLayer;
357 : /**
358 : * @copydoc template <typename PropsType, typename NodeType> void
359 : * Exporter::saveTflResult(const PropsType &props, const NodeType *self);
360 : */
361 : template <>
362 : void Exporter::saveTflResult(const std::tuple<> &props,
363 : const AdditionLayer *self);
364 : #endif
365 :
366 : /**
367 : * @brief base case of iterate_prop, iterate_prop iterates the given tuple
368 : *
369 : * @tparam I size of tuple(automated)
370 : * @tparam Callable generic lambda to be called during iteration
371 : * @tparam Ts types from tuple
372 : * @param c callable generic lambda
373 : * @param tup tuple to be iterated
374 : * @return void
375 : */
376 : template <size_t I = 0, typename Callable, typename... Ts>
377 : typename std::enable_if<I == sizeof...(Ts), void>::type
378 : iterate_prop(Callable &&c, const std::tuple<Ts...> &tup) {
379 : // end of recursion;
380 : }
381 :
382 : /**
383 : * @brief base case of iterate_prop, iterate_prop iterates the given tuple
384 : *
385 : * @tparam I size of tuple(automated)
386 : * @tparam Callable generic lambda to be called during iteration
387 : * @tparam Ts types from tuple
388 : * @param c callable generic lambda
389 : * @param tup tuple to be iterated
390 : * @return not used
391 : */
392 : template <size_t I = 0, typename Callable, typename... Ts>
393 : typename std::enable_if<(I < sizeof...(Ts)), void>::type
394 21316 : iterate_prop(Callable &&c, const std::tuple<Ts...> &tup) {
395 25718 : c(std::get<I>(tup), I);
396 :
397 18893 : iterate_prop<I + 1>(c, tup);
398 21316 : }
399 :
400 : /**
401 : * @copydoc template <size_t I = 0, typename Callable, typename... Ts>
402 : typename std::enable_if<(I < sizeof...(Ts)), void>::type iterate_prop(Callable
403 : &&c, const std::tuple<Ts...> &tup)
404 : */
405 : template <size_t I = 0, typename Callable, typename... Ts>
406 : typename std::enable_if<(I < sizeof...(Ts)), void>::type
407 221040 : iterate_prop(Callable &&c, std::tuple<Ts...> &tup) {
408 267701 : c(std::get<I>(tup), I);
409 :
410 175552 : iterate_prop<I + 1>(c, tup);
411 261085 : }
412 :
413 : /**
414 : * @brief load property from the api formatted string ({"key=value",
415 : * "key1=value1"})
416 : *
417 : * @tparam Tuple tuple type
418 : * @param string_vector api formatted string;
419 : * @param[out] props props to be iterated
420 : * @return std::vector<std::string> vector of string that is not used while
421 : * setting the property
422 : */
423 : template <typename Tuple>
424 : std::vector<std::string>
425 92255 : loadProperties(const std::vector<std::string> &string_vector, Tuple &&props) {
426 : std::vector<std::string> string_vector_splited;
427 92255 : string_vector_splited.reserve(string_vector.size());
428 195115 : for (auto &item : string_vector) {
429 102860 : auto splited = split(item, std::regex("\\|"));
430 102860 : string_vector_splited.insert(string_vector_splited.end(), splited.begin(),
431 : splited.end());
432 : }
433 :
434 : std::vector<std::pair<std::string, std::string>> left;
435 92255 : left.reserve(string_vector_splited.size());
436 92255 : std::transform(string_vector_splited.begin(), string_vector_splited.end(),
437 102876 : std::back_inserter(left), [](const std::string &property) {
438 : std::string key, value;
439 102876 : int status = getKeyValue(property, key, value);
440 102982 : NNTR_THROW_IF(status != ML_ERROR_NONE, std::invalid_argument)
441 : << "parsing property failed, original format: \""
442 : << property << "\"";
443 102770 : return std::make_pair(key, value);
444 : });
445 :
446 580844 : auto callable = [&left](auto &&prop, size_t index) {
447 488731 : std::string prop_key = getPropKey(prop);
448 :
449 953568 : for (auto iter = left.begin(); iter < left.end();) {
450 464873 : if (istrequal(prop_key, iter->first) == true) {
451 60408 : from_string(iter->second, prop);
452 60372 : iter = left.erase(iter);
453 : } else {
454 : iter++;
455 : }
456 : }
457 : };
458 :
459 45488 : iterate_prop(callable, props);
460 :
461 : std::vector<std::string> remainder;
462 92113 : remainder.reserve(left.size());
463 :
464 92113 : std::transform(left.begin(), left.end(), std::back_inserter(remainder),
465 42356 : [](const decltype(left)::value_type &v) {
466 84712 : return v.first + "=" + v.second;
467 : });
468 :
469 92113 : return remainder;
470 92397 : }
471 :
472 : } // namespace nntrainer
473 : #endif // __NODE_EXPORTER_H__
|