1 // SPDX-License-Identifier: Apache-2.0
3 * Copyright (C) 2020 Jijoong Moon <jijoong.moon@samsung.com>
5 * @file network_graph.h
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
14 #ifndef __NETWORK_GRAPH_H__
15 #define __NETWORK_GRAPH_H__
24 #include <execution_mode.h>
25 #include <graph_core.h>
26 #include <layer_node.h>
33 * @class NeuralNetwork Graph Class
34 * @brief NeuralNetwork Graph Class which manage layers
40 * @brief Constructor of NeuralNetwork Graph Class
43 tensor_manager(std::make_shared<Manager>()),
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") {}
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
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_)),
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_) {}
78 * @brief Destructor of the NeuralNetwork Graph class
81 ~NetworkGraph() = default;
84 * @brief Compile the graph
85 * @param[in] loss_type loss for the graph
86 * returns ML_ERROR_NONE on success, error on failure
88 int compile(const std::string &loss_type);
91 * @brief Create new LayerNode and add into Graph
92 * @param[in] layer shared_ptr of Layer
94 void addLayer(std::shared_ptr<LayerNode> layer);
97 * @brief get current flat graph from the model before sorting
98 * @note graph contains pointer to the actual nodes, which is not deeply
100 * @retval current flat graph
102 * @todo remove getting unsorted layers from model loader, compile model
105 std::vector<std::shared_ptr<LayerNode>>
106 getUnsortedLayers(const std::string &input_layer,
107 const std::string &output_layer) const;
110 * @brief getter of number of nodes
111 * @param[out] number of nodes
113 unsigned int size() const { return graph.size(); }
116 * @brief get if the graph is empty
117 * @param[out] true if empty, else false
119 bool empty() const { return graph.empty(); }
122 * @brief Swap function for the class
124 friend void swap(NetworkGraph &lhs, NetworkGraph &rhs) {
125 /// @fixme this swap function need maintenance
128 swap(lhs.graph, rhs.graph);
132 * @brief getter of Sorted LayerNode with index number
136 std::shared_ptr<LayerNode> getSortedLayerNode(unsigned int ith) const {
137 return std::static_pointer_cast<LayerNode>(graph.getSortedNode(ith));
141 * @brief getter of LayerNode with layer name
142 * @param[in] layer name
145 std::shared_ptr<LayerNode> getLayerNode(const std::string &layer_name) const {
146 return std::static_pointer_cast<LayerNode>(graph.getNode(layer_name));
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.
155 std::vector<std::shared_ptr<LayerNode>> getLayerNodes() const;
158 * @brief set batch size
159 * @param[in] batch size
161 void setBatchSize(unsigned int batch_size);
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
168 * @param node node to try apply gradient
169 * @param apply_func apply function
171 static void applyGradients(LayerNode *node,
172 const std::function<void(Weight &)> &apply_func);
175 * @brief forwarding network graph
176 * @param[in] training true if forwarding is on training
177 * @retval output tensors
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);
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
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;
202 * @brief get begin iterator for the graph
203 * @retval const iterator
205 graph_const_iterator<LayerNode> cbegin() const {
206 return graph.cbegin<LayerNode>();
210 * @brief get end iterator for the graph
211 * @retval const iterator
213 graph_const_iterator<LayerNode> cend() const {
214 return graph.cend<LayerNode>();
218 * @brief get reverse begin iterator for the graph
219 * @retval const reverse iterator
221 graph_const_reverse_iterator<LayerNode> crbegin() const {
222 return graph.crbegin<LayerNode>();
226 * @brief get reverse end iterator for the graph
227 * @retval const reverse iterator
229 graph_const_reverse_iterator<LayerNode> crend() const {
230 return graph.crend<LayerNode>();
234 * @brief get begin iterator for the backwarding
235 * @retval const reverse iterator marking the begin of backwarding
237 graph_const_reverse_iterator<LayerNode> getBackwardingBeginIter() const {
242 * @brief get end iterator for the backwarding
243 * @retval const reverse iterator marking the end of backwarding
245 graph_const_reverse_iterator<LayerNode> getBackwardingEndIter() const {
250 * @brief getter of output dimension of graph
251 * @retval output tensor dim list
253 std::vector<TensorDim> getOutputDimension() const;
256 * @brief getter of input dimension of graph
257 * @retval input tensor dim list
259 std::vector<TensorDim> getInputDimension() const;
262 * @brief Get the Batch Size object of current model
264 * @return unsigned int
266 unsigned int getBatchSize() const;
269 * @brief Copy the graph
270 * @param[in] from Graph Object to copy
271 * @retval Graph Object copyed
273 NetworkGraph ©(NetworkGraph &from) {
274 graph.copy(from.graph);
279 * @brief initialize network graph
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
287 int initialize(const std::vector<Connection> &model_input_names = {},
288 const std::vector<Connection> &model_label_names = {});
291 * @brief Create run layer context from the given init layer context
293 * @param lnode layer node to finalize and set run context
294 * @param prev_inputs previous input information
296 std::vector<Var_Grad *>
297 finalizeContext(const std::shared_ptr<LayerNode> &lnode,
298 const std::vector<Var_Grad *> &prev_inputs);
300 /** Interface for manager */
303 * @brief Allocate memory for all the managed tensors
305 * @param[in] training If true, initialize derivates/gradients, else, do not.
307 void allocateTensors(ExecutionMode exec_mode_);
310 * @brief Deallocate memory for all the managed tensors
312 void deallocateTensors(bool dealloc_weights = false) {
313 tensor_manager->deallocateTensors(dealloc_weights);
317 * @brief Allocate memory for all the managed weights
319 void allocateWeights() {
320 tensor_manager->allocateWeights(
321 std::get<3>(backward_iter_end->getExecutionOrder()));
325 * @brief Deallocate memory for all the weights
327 void deallocateWeights() { tensor_manager->deallocateWeights(); }
330 * @brief Enable the memory optimizations for the network
332 * @param val true to enable, else false
334 void setMemoryOptimizations(bool val) {
335 tensor_manager->setOptimizations(val);
336 optimize_memory = val;
340 * @brief Create optimizer variable for every weights
342 * @param cb Call back function which will return vector of dimension
343 * @param request_only_trainable true when only request trainable weight
345 void requestOptimizerVariable(
346 std::function<std::vector<TensorDim>(const TensorDim &)> cb,
347 bool request_only_trainable = true);
350 * @brief Feed inputs and labels to the graph
352 * @param inputs Input data
353 * @param labels Label data
355 void setInputsLabels(const std::vector<Tensor> &inputs,
356 const std::vector<Tensor> &labels);
359 * @brief Feed inputs and labels to the graph
361 * @param inputs Input data
362 * @param labels Label data
364 void setInputsLabels(sharedConstTensors &inputs, sharedConstTensors &labels);
367 * @brief Get the Output Tensors list for the graph
369 * @return std::vector<Tensor> List of output tensors
370 * @note this tensor list is analogous to the label list
372 std::vector<Tensor> getOutputTensors() const;
375 * @brief return model tensor type
377 * @return TensorDim::Format NCHW or NHWC
379 std::array<const std::string, 2> getTensorType() {
380 return {tensor_format, tensor_dtype};
384 * @brief Flush data to the device
390 * @brief Flush data to the device except order
392 * @param order except execution order
394 void flushCacheExcept(const unsigned int order);
398 * @brief Get layer node's tenexecution orders
400 * @param lnode layer node
401 * @note this is for test purpose only
403 std::map<std::string, std::vector<unsigned int>>
404 getLayerExecutionOrders(const std::shared_ptr<LayerNode> &lnode);
405 #endif // ENABLE_TEST
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 */
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
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
424 /// @note *_list and *_dims must be synced at all times. Consider put it as a
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 */
432 bool optimize_memory; /**< optimize memory */
433 ExecutionMode exec_mode; /**< execution mode with which the graph has been
434 currently set or previously set */
436 std::string tensor_format; /**< Model Tensor Format: NCHW or NHWC */
438 std::string tensor_dtype; /**< Model Tensor Type: FP32, FP16 */
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 */
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.
451 void topologicalSortUtil(unsigned int ith, std::vector<bool> &visited,
452 std::stack<std::shared_ptr<LayerNode>> &Stack);
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.
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
466 int checkCompiledGraph();
469 * @brief mark nodes required for backwarding.
471 void markNodesForBackwarding();
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.
479 int addLossLayer(const std::string &loss_type);
482 * @brief set output connections for all the layers
484 void setOutputConnections();
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.
495 void ensureName(std::shared_ptr<Layer> layer, const std::string &prefix = "",
496 const std::string &postfix = "", bool force_rename = false);
499 * @brief Create new LayerNode and add into Graph
500 * @param[in] layer shared_ptr of Layer
502 void addLayerNode(std::unique_ptr<Layer> layer);
505 * @brief finalize already added loss layers
507 * @details This involves verify if the requirements of the added loss layers
508 * match and merging loss layers with activation layers if needed.
510 void finalizeLossLayer();
513 * @brief Set the order of execution for all the nodes in the graph
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().
520 void setExecutionOrder();
523 * @brief Set external data to the given tensors with name
525 * @param data External data
526 * @param names Names of the tensor to set the data to
528 void setExternalTensors(const std::vector<Tensor> &data,
529 const std::vector<std::string> names);
532 * @brief Optimize the graph memory utilization for in-place operations
534 void inPlaceOptimize();
537 * @brief Check if the given node can execute in-place
539 * @param lnode node to check for in-place execution
541 * @return the mode of inplace for the layer
543 InPlace canExecuteInPlace(const std::shared_ptr<LayerNode> &lnode);
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.
550 * @return end of the backward iter;
552 LayerNode *computeBackwardEnd();
555 } // namespace nntrainer
557 #endif /* __cplusplus */
558 #endif /* __NETWORK_GRAPH_H__ */