[ Mixed Tensor ] Enable FP32 unittest cases
[platform/core/ml/nntrainer.git] / nntrainer / graph / network_graph.h
1 // SPDX-License-Identifier: Apache-2.0
2 /**
3  * Copyright (C) 2020 Jijoong Moon <jijoong.moon@samsung.com>
4  *
5  * @file    network_graph.h
6  * @date    19 Oct 2020
7  * @see     https://github.com/nnstreamer/nntrainer
8  * @author  Jijoong Moon <jijoong.moon@samsung.com>
9  * @bug     No known bugs except for NYI items
10  * @brief   This is Network Graph Class for Neural Network
11  *
12  */
13
14 #ifndef __NETWORK_GRAPH_H__
15 #define __NETWORK_GRAPH_H__
16 #ifdef __cplusplus
17
18 #include <list>
19 #include <map>
20 #include <memory>
21 #include <stack>
22 #include <vector>
23
24 #include <execution_mode.h>
25 #include <graph_core.h>
26 #include <layer_node.h>
27 #include <manager.h>
28
29 namespace nntrainer {
30
31 class Connection;
32 /**
33  * @class   NeuralNetwork Graph Class
34  * @brief   NeuralNetwork Graph Class which manage layers
35  */
36 class NetworkGraph {
37
38 public:
39   /**
40    * @brief     Constructor of NeuralNetwork Graph Class
41    */
42   NetworkGraph() :
43     tensor_manager(std::make_shared<Manager>()),
44     graph(),
45     compiled(false),
46     batch_size(0),
47     graph_exec_end(0),
48     backward_iter_end(nullptr),
49     forward_iter_end(nullptr),
50     optimize_memory(true),
51     exec_mode(ExecutionMode::TRAIN),
52     tensor_format("NCHW"),
53     tensor_dtype("FP32") {}
54
55   /**
56    * @brief     Constructor of NeuralNetwork Graph Class
57    * @param[in] enable_swap enable memory swap for tensor
58    * @param[in] swap_path memory swap file path when the swap is enabled
59    */
60   NetworkGraph(bool enable_swap, const std::string &swap_path = "",
61                unsigned int lookahead = 0,
62                const std::string &tensor_format_ = "NCHW",
63                const std::string &tensor_dtype_ = "FP32") :
64     tensor_manager(std::make_shared<Manager>(enable_swap, swap_path, lookahead,
65                                              tensor_format_, tensor_dtype_)),
66     graph(),
67     compiled(false),
68     batch_size(0),
69     graph_exec_end(0),
70     backward_iter_end(nullptr),
71     forward_iter_end(nullptr),
72     optimize_memory(true),
73     exec_mode(ExecutionMode::TRAIN),
74     tensor_format(tensor_format_),
75     tensor_dtype(tensor_dtype_) {}
76
77   /**
78    * @brief   Destructor of the NeuralNetwork Graph class
79    *
80    */
81   ~NetworkGraph() = default;
82
83   /**
84    * @brief     Compile the graph
85    * @param[in] loss_type loss for the graph
86    * returns ML_ERROR_NONE on success, error on failure
87    */
88   int compile(const std::string &loss_type);
89
90   /**
91    * @brief Create new LayerNode and add into Graph
92    * @param[in] layer shared_ptr of Layer
93    */
94   void addLayer(std::shared_ptr<LayerNode> layer);
95
96   /**
97    * @brief get current flat graph from the model before sorting
98    * @note graph contains pointer to the actual nodes, which is not deeply
99    * copied.
100    * @retval current flat graph
101    *
102    * @todo remove getting unsorted layers from model loader, compile model
103    * loader
104    */
105   std::vector<std::shared_ptr<LayerNode>>
106   getUnsortedLayers(const std::string &input_layer,
107                     const std::string &output_layer) const;
108
109   /**
110    * @brief getter of number of nodes
111    * @param[out] number of nodes
112    */
113   unsigned int size() const { return graph.size(); }
114
115   /**
116    * @brief get if the graph is empty
117    * @param[out] true if empty, else false
118    */
119   bool empty() const { return graph.empty(); }
120
121   /**
122    * @brief     Swap function for the class
123    */
124   friend void swap(NetworkGraph &lhs, NetworkGraph &rhs) {
125     /// @fixme this swap function need maintenance
126     using std::swap;
127
128     swap(lhs.graph, rhs.graph);
129   }
130
131   /**
132    * @brief getter of Sorted LayerNode with index number
133    * @param[in] index
134    * @ret LayerNode
135    */
136   std::shared_ptr<LayerNode> getSortedLayerNode(unsigned int ith) const {
137     return std::static_pointer_cast<LayerNode>(graph.getSortedNode(ith));
138   }
139
140   /**
141    * @brief getter of LayerNode with layer name
142    * @param[in] layer name
143    * @retval LayerNode
144    */
145   std::shared_ptr<LayerNode> getLayerNode(const std::string &layer_name) const {
146     return std::static_pointer_cast<LayerNode>(graph.getNode(layer_name));
147   }
148
149   /**
150    * @brief getter all the layer nodes in the model
151    * @retval Layer nodes
152    * @note these layer nodes will be in sorted order if the model is compiled,
153    * otherwise the order is the order of addition of layer nodes in the model.
154    */
155   std::vector<std::shared_ptr<LayerNode>> getLayerNodes() const;
156
157   /**
158    * @brief     set batch size
159    * @param[in] batch size
160    */
161   void setBatchSize(unsigned int batch_size);
162
163   /**
164    * @brief try apply gradient if possible
165    * @note if it is not the last of the gradient access, this is noop
166    * @note if the gradient is to be clipped by norm, this is noop
167    *
168    * @param node node to try apply gradient
169    * @param apply_func apply function
170    */
171   static void applyGradients(LayerNode *node,
172                              const std::function<void(Weight &)> &apply_func);
173
174   /**
175    * @brief     forwarding network graph
176    * @param[in] training true if forwarding is on training
177    * @retval output tensors
178    */
179   sharedConstTensors forwarding(
180     bool training = false,
181     std::function<void(std::shared_ptr<LayerNode>, bool)> forwarding_op =
182       [](std::shared_ptr<LayerNode>, bool) {},
183     std::function<bool(void *userdata)> stop_cb =
184       [](void *user_data) { return false; },
185     void *user_data = nullptr);
186
187   /**
188    * @brief     backwarding the network graph
189    * @param[in] iteration current iteration number
190    * @param[in] backwarding_op operation for the backwarding
191    * @param[in] apply_grad_clip_op operation for applying the clip gradients
192    */
193   void backwarding(
194     int iteration,
195     std::function<void(std::shared_ptr<LayerNode>, int)> &backwarding_op,
196     std::function<void(Weight &, int)> &apply_grad_clip_op,
197     std::function<bool(void *userdata)> stop_cb =
198       [](void *user_data) { return false; },
199     void *user_data = nullptr) const;
200
201   /**
202    * @brief     get begin iterator for the graph
203    * @retval    const iterator
204    */
205   graph_const_iterator<LayerNode> cbegin() const {
206     return graph.cbegin<LayerNode>();
207   }
208
209   /**
210    * @brief     get end iterator for the graph
211    * @retval    const iterator
212    */
213   graph_const_iterator<LayerNode> cend() const {
214     return graph.cend<LayerNode>();
215   }
216
217   /**
218    * @brief     get reverse begin iterator for the graph
219    * @retval    const reverse iterator
220    */
221   graph_const_reverse_iterator<LayerNode> crbegin() const {
222     return graph.crbegin<LayerNode>();
223   }
224
225   /**
226    * @brief     get reverse end iterator for the graph
227    * @retval    const reverse iterator
228    */
229   graph_const_reverse_iterator<LayerNode> crend() const {
230     return graph.crend<LayerNode>();
231   }
232
233   /**
234    * @brief     get begin iterator for the backwarding
235    * @retval    const reverse iterator marking the begin of backwarding
236    */
237   graph_const_reverse_iterator<LayerNode> getBackwardingBeginIter() const {
238     return crbegin();
239   }
240
241   /**
242    * @brief     get end iterator for the backwarding
243    * @retval    const reverse iterator marking the end of backwarding
244    */
245   graph_const_reverse_iterator<LayerNode> getBackwardingEndIter() const {
246     return crend();
247   }
248
249   /**
250    * @brief     getter of output dimension of graph
251    * @retval    output tensor dim list
252    */
253   std::vector<TensorDim> getOutputDimension() const;
254
255   /**
256    * @brief     getter of input dimension of graph
257    * @retval    input tensor dim list
258    */
259   std::vector<TensorDim> getInputDimension() const;
260
261   /**
262    * @brief Get the Batch Size object of current model
263    *
264    * @return unsigned int
265    */
266   unsigned int getBatchSize() const;
267
268   /**
269    * @brief     Copy the graph
270    * @param[in] from Graph Object to copy
271    * @retval    Graph Object copyed
272    */
273   NetworkGraph &copy(NetworkGraph &from) {
274     graph.copy(from.graph);
275     return *this;
276   }
277
278   /**
279    * @brief initialize network graph
280    *
281    * @param model_input_names model input connection if empty list given, all of
282    * node that can be inputs will be identified in the sort order
283    * @param model_label_names model label names if empty list given, all of node
284    * that can be labels will be identified in the sort order
285    * @return int ML_ERROR_NONE if successful
286    */
287   int initialize(const std::vector<Connection> &model_input_names = {},
288                  const std::vector<Connection> &model_label_names = {});
289
290   /**
291    * @brief Create run layer context from the given init layer context
292    *
293    * @param lnode layer node to finalize and set run context
294    * @param prev_inputs previous input information
295    */
296   std::vector<Var_Grad *>
297   finalizeContext(const std::shared_ptr<LayerNode> &lnode,
298                   const std::vector<Var_Grad *> &prev_inputs);
299
300   /** Interface for manager */
301
302   /**
303    * @brief Allocate memory for all the managed tensors
304    *
305    * @param[in] training If true, initialize derivates/gradients, else, do not.
306    */
307   void allocateTensors(ExecutionMode exec_mode_);
308
309   /**
310    * @brief Deallocate memory for all the managed tensors
311    */
312   void deallocateTensors(bool dealloc_weights = false) {
313     tensor_manager->deallocateTensors(dealloc_weights);
314   }
315
316   /**
317    * @brief Allocate memory for all the managed weights
318    */
319   void allocateWeights() {
320     tensor_manager->allocateWeights(
321       std::get<3>(backward_iter_end->getExecutionOrder()));
322   }
323
324   /**
325    * @brief Deallocate memory for all the weights
326    */
327   void deallocateWeights() { tensor_manager->deallocateWeights(); }
328
329   /**
330    * @brief     Enable the memory optimizations for the network
331    *
332    * @param val true to enable, else false
333    */
334   void setMemoryOptimizations(bool val) {
335     tensor_manager->setOptimizations(val);
336     optimize_memory = val;
337   }
338
339   /**
340    * @brief     Create optimizer variable for every weights
341    *
342    * @param cb  Call back function which will return vector of dimension
343    * @param request_only_trainable true when only request trainable weight
344    */
345   void requestOptimizerVariable(
346     std::function<std::vector<TensorDim>(const TensorDim &)> cb,
347     bool request_only_trainable = true);
348
349   /**
350    * @brief Feed inputs and labels to the graph
351    *
352    * @param inputs Input data
353    * @param labels Label data
354    */
355   void setInputsLabels(const std::vector<Tensor> &inputs,
356                        const std::vector<Tensor> &labels);
357
358   /**
359    * @brief Feed inputs and labels to the graph
360    *
361    * @param inputs Input data
362    * @param labels Label data
363    */
364   void setInputsLabels(sharedConstTensors &inputs, sharedConstTensors &labels);
365
366   /**
367    * @brief Get the Output Tensors list for the graph
368    *
369    * @return std::vector<Tensor> List of output tensors
370    * @note this tensor list is analogous to the label list
371    */
372   std::vector<Tensor> getOutputTensors() const;
373
374   /**
375    * @brief return model tensor type
376    *
377    * @return TensorDim::Format NCHW or NHWC
378    */
379   std::array<const std::string, 2> getTensorType() {
380     return {tensor_format, tensor_dtype};
381   };
382
383   /**
384    * @brief Flush data to the device
385    *
386    */
387   void flushCache();
388
389   /**
390    * @brief Flush data to the device except order
391    *
392    * @param order except execution order
393    */
394   void flushCacheExcept(const unsigned int order);
395
396 #ifdef ENABLE_TEST
397   /**
398    * @brief Get layer node's tenexecution orders
399    *
400    * @param lnode layer node
401    * @note this is for test purpose only
402    */
403   std::map<std::string, std::vector<unsigned int>>
404   getLayerExecutionOrders(const std::shared_ptr<LayerNode> &lnode);
405 #endif // ENABLE_TEST
406
407 private:
408   std::map<std::string, std::string> sub_in_out; /** This is map to identify
409                    input and output layer name of subgraph */
410   std::shared_ptr<Manager> tensor_manager;       /**< tensors manager */
411
412   GraphCore graph;             /** core graph object */
413   bool compiled;               /**< if the model graph is compiled */
414   unsigned int batch_size;     /**< current batch_size */
415   unsigned int graph_exec_end; /**< Inclusive, last execution order of the
416                                   given graph */
417   LayerNode
418     *backward_iter_end;        /**< inclusive end node of the valid backward
419                                   execution when initialized, nodes after this node
420                                   does not required backwarding thus making it noop */
421   LayerNode *forward_iter_end; /**< inclusive end node of the forward execution
422                                   when initialize */
423
424   /// @note *_list and *_dims must be synced at all times. Consider put it as a
425   /// structure
426   std::vector<std::string> label_list;  /**< identifier for the model labels */
427   std::vector<std::string> input_list;  /**< identifier for the model inputs */
428   std::vector<std::string> output_list; /**< identifier for the model outputs */
429   std::vector<TensorDim> label_dims;    /**< graph label dimensions */
430   std::vector<TensorDim> input_dims;    /**< graph input dimensions */
431
432   bool optimize_memory;    /**< optimize memory */
433   ExecutionMode exec_mode; /**< execution mode with which the graph has been
434                               currently set or previously set */
435
436   std::string tensor_format; /**< Model Tensor Format: NCHW or NHWC */
437
438   std::string tensor_dtype; /**< Model Tensor Type: FP32, FP16 */
439
440   std::unordered_map<std::string, int>
441     profile_keys; /**< profile keys based on the layer type */
442   std::vector<Weight *>
443     clip_weights; /**< weights with global norm based clipping enabled */
444
445   /**
446    * @brief     topological sort
447    * @param[in] ith index of LayerNode
448    * @param[in] visited temp list
449    * @param[in] stack for Node list to visit.
450    */
451   void topologicalSortUtil(unsigned int ith, std::vector<bool> &visited,
452                            std::stack<std::shared_ptr<LayerNode>> &Stack);
453
454   /**
455    * @brief     check if graph is ready to compile.
456    * @retval #ML_ERROR_NONE graph is ready to compile
457    * @retval #ML_ERROR_INVALID_PARAMETER not ready to compile.
458    */
459   int isCompilable();
460
461   /**
462    * @brief     check if the compiled graph is of correct form.
463    * @retval #ML_ERROR_NONE graph is compiled correctly
464    * @retval #ML_ERROR_INVALID_PARAMETER did not compile correctly
465    */
466   int checkCompiledGraph();
467
468   /**
469    * @brief     mark nodes required for backwarding.
470    */
471   void markNodesForBackwarding();
472
473   /**
474    * @brief     adding loss layer at last position
475    * @param[in] loss_type loss type
476    * @retval #ML_ERROR_NONE Successful.
477    * @retval #ML_ERROR_INVALID_PARAMETER invalid parameter.
478    */
479   int addLossLayer(const std::string &loss_type);
480
481   /**
482    * @brief     set output connections for all the layers
483    */
484   void setOutputConnections();
485
486   /**
487    * @brief     Ensure that layer has a name.
488    * @param[in] layer Layer whose name is to be ensured to be valid
489    * @param[in] prefix Prefix to be attached to the layer name
490    * @param[in] postfix Postfix to be attached to the layer name
491    * @param[in] force_rename If the layer must be forcefully rename
492    * @details   Ensures that the layer has a unique and a valid name. A valid
493    * name pre-assigned to the layer can be changed if force_rename is enabled.
494    */
495   void ensureName(std::shared_ptr<Layer> layer, const std::string &prefix = "",
496                   const std::string &postfix = "", bool force_rename = false);
497
498   /**
499    * @brief Create new LayerNode and add into Graph
500    * @param[in] layer shared_ptr of Layer
501    */
502   void addLayerNode(std::unique_ptr<Layer> layer);
503
504   /**
505    * @brief finalize already added loss layers
506    *
507    * @details This involves verify if the requirements of the added loss layers
508    * match and merging loss layers with activation layers if needed.
509    */
510   void finalizeLossLayer();
511
512   /**
513    * @brief Set the order of execution for all the nodes in the graph
514    *
515    * @details This sets the order of execution using the order from the
516    * topological sort. The order of forwarding matches the topological sort. The
517    * order for backwarding is in the exact reverse order. The calcDerivative()
518    * is expected to be called right after calcGradient().
519    */
520   void setExecutionOrder();
521
522   /**
523    * @brief Set external data to the given tensors with name
524    *
525    * @param data External data
526    * @param names Names of the tensor to set the data to
527    */
528   void setExternalTensors(const std::vector<Tensor> &data,
529                           const std::vector<std::string> names);
530
531   /**
532    * @brief     Optimize the graph memory utilization for in-place operations
533    */
534   void inPlaceOptimize();
535
536   /**
537    * @brief     Check if the given node can execute in-place
538    *
539    * @param lnode node to check for in-place execution
540    *
541    * @return the mode of inplace for the layer
542    */
543   InPlace canExecuteInPlace(const std::shared_ptr<LayerNode> &lnode);
544
545   /**
546    * @brief compute optimized backward end. This function calculated the valid
547    * end of the graph backward, if memory_optimize is unset, this returns
548    * beginning of the graph node.
549    *
550    * @return end of the backward iter;
551    */
552   LayerNode *computeBackwardEnd();
553 };
554
555 } // namespace nntrainer
556
557 #endif /* __cplusplus */
558 #endif /* __NETWORK_GRAPH_H__ */