Line data Source code
1 : // SPDX-License-Identifier: Apache-2.0
2 : /**
3 : * Copyright (C) 2020 Parichay Kapoor <pk.kapoor@samsung.com>
4 : *
5 : * @file layer.h
6 : * @date 14 October 2020
7 : * @see https://github.com/nnstreamer/nntrainer
8 : * @author Parichay Kapoor <pk.kapoor@samsung.com>
9 : * @bug No known bugs except for NYI items
10 : * @brief This is layers interface for c++ API
11 : *
12 : * @note This is experimental API and not stable.
13 : */
14 :
15 : #ifndef __ML_TRAIN_LAYER_H__
16 : #define __ML_TRAIN_LAYER_H__
17 :
18 : #if __cplusplus < MIN_CPP_VERSION
19 : #error "CPP versions c++17 or over are only supported"
20 : #endif // __cpluscplus
21 :
22 : #include <memory>
23 : #include <string>
24 : #include <tensor_dim.h>
25 : #include <vector>
26 :
27 : #include <common.h>
28 :
29 : namespace ml {
30 : namespace train {
31 :
32 : /**
33 : * @brief Enumeration of layer type
34 : */
35 : enum LayerType {
36 : LAYER_IN = ML_TRAIN_LAYER_TYPE_INPUT, /**< Input Layer type */
37 : LAYER_FC = ML_TRAIN_LAYER_TYPE_FC, /**< Fully Connected Layer type */
38 : LAYER_BN = ML_TRAIN_LAYER_TYPE_BN, /**< Batch Normalization Layer type */
39 : LAYER_CONV2D = ML_TRAIN_LAYER_TYPE_CONV2D, /**< Convolution 2D Layer type */
40 : LAYER_POOLING2D = ML_TRAIN_LAYER_TYPE_POOLING2D, /**< Pooling 2D Layer type */
41 : LAYER_FLATTEN = ML_TRAIN_LAYER_TYPE_FLATTEN, /**< Flatten Layer type */
42 : LAYER_ACTIVATION =
43 : ML_TRAIN_LAYER_TYPE_ACTIVATION, /**< Activation Layer type */
44 : LAYER_ADDITION = ML_TRAIN_LAYER_TYPE_ADDITION, /**< Addition Layer type */
45 : LAYER_CONCAT = ML_TRAIN_LAYER_TYPE_CONCAT, /**< Concat Layer type */
46 : LAYER_MULTIOUT = ML_TRAIN_LAYER_TYPE_MULTIOUT, /**< Multi Output Layer type */
47 : LAYER_EMBEDDING = ML_TRAIN_LAYER_TYPE_EMBEDDING, /**< Embedding Layer type */
48 : LAYER_RNN = ML_TRAIN_LAYER_TYPE_RNN, /**< RNN Layer type */
49 : LAYER_LSTM = ML_TRAIN_LAYER_TYPE_LSTM, /**< LSTM Layer type */
50 : LAYER_SPLIT = ML_TRAIN_LAYER_TYPE_SPLIT, /**< Splite Layer type */
51 : LAYER_GRU = ML_TRAIN_LAYER_TYPE_GRU, /**< GRU Layer type */
52 : LAYER_PERMUTE = ML_TRAIN_LAYER_TYPE_PERMUTE, /**< Permute layer */
53 : LAYER_DROPOUT = ML_TRAIN_LAYER_TYPE_DROPOUT, /**< DropOut Layer type */
54 : LAYER_BACKBONE_NNSTREAMER =
55 : ML_TRAIN_LAYER_TYPE_BACKBONE_NNSTREAMER, /**< Backbone using NNStreamer */
56 : LAYER_CENTROID_KNN =
57 : ML_TRAIN_LAYER_TYPE_CENTROID_KNN, /**< Centroid KNN Layer */
58 : LAYER_CONV1D = ML_TRAIN_LAYER_TYPE_CONV1D, /**< Convolution 1D Layer type */
59 : LAYER_LSTMCELL = ML_TRAIN_LAYER_TYPE_LSTMCELL, /**< LSTM Cell Layer type */
60 : LAYER_GRUCELL = ML_TRAIN_LAYER_TYPE_GRUCELL, /**< GRU Cell Layer type */
61 : LAYER_RNNCELL = ML_TRAIN_LAYER_TYPE_RNNCELL, /**< RNN Cell Layer type */
62 : LAYER_ZONEOUT_LSTMCELL =
63 : ML_TRAIN_LAYER_TYPE_ZONEOUTLSTMCELL, /**< Zoneout LSTM Cell Layer type */
64 : LAYER_ATTENTION = ML_TRAIN_LAYER_TYPE_ATTENTION, /**< Attention Layer type */
65 : LAYER_MOL_ATTENTION =
66 : ML_TRAIN_LAYER_TYPE_MOL_ATTENTION, /**< MoL Attention Layer type */
67 : LAYER_MULTI_HEAD_ATTENTION =
68 : ML_TRAIN_LAYER_TYPE_MULTI_HEAD_ATTENTION, /**< Multi Head Attention Layer
69 : type */
70 : LAYER_LAYER_NORMALIZATION =
71 : ML_TRAIN_LAYER_TYPE_LAYER_NORMALIZATION, /**< Layer Normalization Layer type
72 : */
73 : LAYER_POSITIONAL_ENCODING =
74 : ML_TRAIN_LAYER_TYPE_POSITIONAL_ENCODING, /**< Positional Encoding Layer type
75 : */
76 : LAYER_IDENTITY = ML_TRAIN_LAYER_TYPE_IDENTITY, /**< Identity Layer type */
77 : LAYER_PREPROCESS_FLIP =
78 : ML_TRAIN_LAYER_TYPE_PREPROCESS_FLIP, /**< Preprocess flip Layer type */
79 : LAYER_PREPROCESS_TRANSLATE =
80 : ML_TRAIN_LAYER_TYPE_PREPROCESS_TRANSLATE, /**< Preprocess translate Layer
81 : type */
82 : LAYER_PREPROCESS_L2NORM =
83 : ML_TRAIN_LAYER_TYPE_PREPROCESS_L2NORM, /**< Preprocess l2norm Layer type */
84 : LAYER_LOSS_MSE =
85 : ML_TRAIN_LAYER_TYPE_LOSS_MSE, /**< Mean Squared Error Loss Layer type */
86 : LAYER_LOSS_CROSS_ENTROPY_SIGMOID =
87 : ML_TRAIN_LAYER_TYPE_LOSS_CROSS_ENTROPY_SIGMOID, /**< Cross Entropy with
88 : Sigmoid Loss Layer type
89 : */
90 : LAYER_LOSS_CROSS_ENTROPY_SOFTMAX =
91 : ML_TRAIN_LAYER_TYPE_LOSS_CROSS_ENTROPY_SOFTMAX, /**< Cross Entropy with
92 : Softmax Loss Layer type
93 : */
94 : LAYER_TIME_DIST, /**< Time Distributed Layer type */
95 : LAYER_BACKBONE_TFLITE, /**< Backbone using TFLite */
96 : LAYER_RESHAPE, /**< Reshape Layer type */
97 : LAYER_REDUCE_MEAN, /**< Reduce mean Layer type */
98 : LAYER_LOSS_CONSTANT_DERIVATIVE, /**< Synthetic loss layer to feed constant
99 : derivative */
100 : LAYER_UNKNOWN = ML_TRAIN_LAYER_TYPE_UNKNOWN /**< Unknown */
101 : };
102 :
103 : /**
104 : * @class Layer Base class for layers
105 : * @brief Base class for all layers
106 : */
107 9516 : class Layer {
108 : public:
109 : /**
110 : * @brief Destructor of Layer Class
111 : */
112 9516 : virtual ~Layer() = default;
113 :
114 : /**
115 : * @brief Get the layer type
116 : * @return const std::string type representation
117 : */
118 : virtual const std::string getType() const = 0;
119 :
120 : /**
121 : * @brief Default allowed properties
122 : * - input shape : string
123 : * - bias zero : bool
124 : * - normalization : bool
125 : * - standardization : bool
126 : * - activation : string (type)
127 : * - epsilon : float
128 : * - weight_regularizer : string (type)
129 : * - weight_regularizer_constant : float
130 : * - unit : int
131 : * - weight_initializer : string (type)
132 : * - filter_size : int
133 : * - kernel_size : ( n , m )
134 : * - stride : ( n, m )
135 : * - padding : ( n, m )
136 : * - pool_size : ( n,m )
137 : * - pooling : max, average, global_max, global_average
138 : * - flatten : bool
139 : * - name : string (type)
140 : * - momentum : float,
141 : * - moving_mean_initializer : string (type),
142 : * - moving_variance_initializer : string (type),
143 : * - gamma_initializer : string (type),
144 : * - beta_initializer" : string (type)
145 : * - modelfile : model file for loading config for backbone layer
146 : * - input_layers : string (type)
147 : * - output_layers : string (type)
148 : * - trainable :
149 : * - flip_direction
150 : * - random_translate
151 : * - in_dim : int ( input dimension for embedding layer )
152 : * - out_dim : int ( output dimesion for embedding layer )
153 : * - recurrent_activation : string (type) - used only in lstm
154 : * - return_sequences : bool (type) - used only in lstm
155 : * - distribute : bool
156 : * - hidden_state_activation : string (type) - used only in lstm
157 : * - drop_out : float (type) - drop out rate
158 : */
159 : /**
160 : * @brief set Property of layer
161 : * @todo change the function signature
162 : * @param[in] values values of property
163 : * @retval #ML_ERROR_NONE Successful.
164 : * @retval #ML_ERROR_INVALID_PARAMETER invalid parameter.
165 : * @details This function accepts vector of properties in the format -
166 : * { std::string property_name, void * property_val, ...}
167 : */
168 : virtual void setProperty(const std::vector<std::string> &values) = 0;
169 :
170 : /**
171 : * @brief Get name of the layer
172 : * @retval name of the layer
173 : * @note This name is unique to this layer in a model
174 : * @note This name might be changed once this layer is added to the model
175 : * to keep the name unique to the model
176 : */
177 : virtual const std::string getName() const noexcept = 0;
178 :
179 : /**
180 : * @brief Get the Weight object name
181 : *
182 : * @param idx Identifier of the weight
183 : * @return const std::string &Name of the weight
184 : */
185 : virtual const std::string &getWeightName(unsigned int idx) = 0;
186 :
187 : /**
188 : * @brief Get weight data of the layer
189 : * @retval weight data of the layer
190 : * @note nntrainer assign the vector and if there is no weights, the size
191 : * of vector is zero
192 : * @note layer needs to be finalized before called.
193 : */
194 : virtual const std::vector<float *> getWeights() = 0;
195 :
196 : /**
197 : * @brief Get weight data of the layer
198 : * @retval weights : float * arrary to store weight data
199 : * @retval weights_dim : TensorDim for each weights
200 : * @note nntrainer assign the vector and if there is no weights, the size
201 : * of vector is zero
202 : * @note layer needs to be finalized before called.
203 : */
204 : virtual void getWeights(std::vector<float *> &weights,
205 : std::vector<ml::train::TensorDim> &weights_dim) = 0;
206 :
207 : /**
208 : * @brief Set weight data of the layer
209 : * @note Size of vector must be the same with number of weights.
210 : * @note layer needs to be finalized before called.
211 : */
212 : virtual void setWeights(const std::vector<float *>) = 0;
213 : };
214 :
215 : /**
216 : * @brief Factory creator with constructor for layer type
217 : */
218 : std::unique_ptr<Layer>
219 : createLayer(const LayerType &type,
220 : const std::vector<std::string> &properties = {});
221 :
222 : /**
223 : * @brief Factory creator with constructor for layer
224 : */
225 : std::unique_ptr<Layer>
226 : createLayer(const std::string &type,
227 : const std::vector<std::string> &properties = {});
228 :
229 : /**
230 : * @brief General Layer Factory function to register Layer
231 : *
232 : * @param props property representation
233 : * @return std::unique_ptr<ml::train::Layer> created object
234 : */
235 : template <typename T,
236 : std::enable_if_t<std::is_base_of<Layer, T>::value, T> * = nullptr>
237 : std::unique_ptr<Layer> createLayer(const std::vector<std::string> &props = {}) {
238 : std::unique_ptr<Layer> ptr = std::make_unique<T>();
239 :
240 : ptr->setProperty(props);
241 : return ptr;
242 : }
243 :
244 : /**
245 : * Aliases for various layers and losses
246 : */
247 : namespace layer {
248 :
249 : /**
250 : * @brief Helper function to create input layer
251 : */
252 : inline std::unique_ptr<Layer>
253 6 : Input(const std::vector<std::string> &properties = {}) {
254 6 : return createLayer(LayerType::LAYER_IN, properties);
255 : }
256 :
257 : /**
258 : * @brief Helper function to create fully connected layer
259 : */
260 : inline std::unique_ptr<Layer>
261 10 : FullyConnected(const std::vector<std::string> &properties = {}) {
262 10 : return createLayer(LayerType::LAYER_FC, properties);
263 : }
264 :
265 : /**
266 : * @brief Helper function to create batch normalization layer
267 : */
268 : inline std::unique_ptr<Layer>
269 1 : BatchNormalization(const std::vector<std::string> &properties = {}) {
270 1 : return createLayer(LayerType::LAYER_BN, properties);
271 : }
272 :
273 : /**
274 : * @brief Helper function to create layer normalization layer
275 : */
276 : inline std::unique_ptr<Layer>
277 : LayerNormalization(const std::vector<std::string> &properties = {}) {
278 : return createLayer(LayerType::LAYER_LAYER_NORMALIZATION, properties);
279 : }
280 :
281 : /**
282 : * @brief Helper function to create convolution 2d layer
283 : */
284 : inline std::unique_ptr<Layer>
285 1 : Convolution2D(const std::vector<std::string> &properties = {}) {
286 1 : return createLayer(LayerType::LAYER_CONV2D, properties);
287 : }
288 :
289 : /**
290 : * @brief Helper function to create convolution 1d layer
291 : */
292 : inline std::unique_ptr<Layer>
293 : Convolution1D(const std::vector<std::string> &properties = {}) {
294 : return createLayer(LayerType::LAYER_CONV1D, properties);
295 : }
296 :
297 : /**
298 : * @brief Helper function to create pooling 2d layer
299 : */
300 : inline std::unique_ptr<Layer>
301 1 : Pooling2D(const std::vector<std::string> &properties = {}) {
302 1 : return createLayer(LayerType::LAYER_POOLING2D, properties);
303 : }
304 :
305 : /**
306 : * @brief Helper function to create flatten layer
307 : */
308 : inline std::unique_ptr<Layer>
309 1 : Flatten(const std::vector<std::string> &properties = {}) {
310 1 : return createLayer(LayerType::LAYER_FLATTEN, properties);
311 : }
312 :
313 : /**
314 : * @brief Helper function to create reshape layer
315 : */
316 : inline std::unique_ptr<Layer>
317 : Reshape(const std::vector<std::string> &properties = {}) {
318 : return createLayer(LayerType::LAYER_RESHAPE, properties);
319 : }
320 :
321 : /**
322 : * @brief Helper function to create addition layer
323 : */
324 : inline std::unique_ptr<Layer>
325 1 : Addition(const std::vector<std::string> &properties = {}) {
326 1 : return createLayer(LayerType::LAYER_ADDITION, properties);
327 : }
328 :
329 : /**
330 : * @brief Helper function to create concat layer
331 : */
332 : inline std::unique_ptr<Layer>
333 1 : Concat(const std::vector<std::string> &properties = {}) {
334 1 : return createLayer(LayerType::LAYER_CONCAT, properties);
335 : }
336 :
337 : /**
338 : * @brief Helper function to create multi-out layer
339 : */
340 : inline std::unique_ptr<Layer>
341 1 : MultiOut(const std::vector<std::string> &properties = {}) {
342 1 : return createLayer(LayerType::LAYER_MULTIOUT, properties);
343 : }
344 :
345 : /**
346 : * @brief Helper function to create nnstreamer backbone layer
347 : */
348 : inline std::unique_ptr<Layer>
349 1 : BackboneNNStreamer(const std::vector<std::string> &properties = {}) {
350 1 : return createLayer(LayerType::LAYER_BACKBONE_NNSTREAMER, properties);
351 : }
352 :
353 : /**
354 : * @brief Helper function to create tflite backbone layer
355 : */
356 : inline std::unique_ptr<Layer>
357 2 : BackboneTFLite(const std::vector<std::string> &properties = {}) {
358 2 : return createLayer(LayerType::LAYER_BACKBONE_TFLITE, properties);
359 : }
360 :
361 : /**
362 : * @brief Helper function to create Embedding layer
363 : */
364 : inline std::unique_ptr<Layer>
365 : Embedding(const std::vector<std::string> &properties = {}) {
366 : return createLayer(LayerType::LAYER_EMBEDDING, properties);
367 : }
368 :
369 : /**
370 : * @brief Helper function to create RNN layer
371 : */
372 : inline std::unique_ptr<Layer>
373 : RNN(const std::vector<std::string> &properties = {}) {
374 : return createLayer(LayerType::LAYER_RNN, properties);
375 : }
376 :
377 : /**
378 : * @brief Helper function to create RNNCell layer
379 : */
380 : inline std::unique_ptr<Layer>
381 : RNNCell(const std::vector<std::string> &properties = {}) {
382 : return createLayer(LayerType::LAYER_RNNCELL, properties);
383 : }
384 :
385 : /**
386 : * @brief Helper function to create LSTM layer
387 : */
388 : inline std::unique_ptr<Layer>
389 : LSTM(const std::vector<std::string> &properties = {}) {
390 : return createLayer(LayerType::LAYER_LSTM, properties);
391 : }
392 :
393 : /**
394 : * @brief Helper function to create LSTMCell layer
395 : */
396 : inline std::unique_ptr<Layer>
397 : LSTMCell(const std::vector<std::string> &properties = {}) {
398 : return createLayer(LayerType::LAYER_LSTMCELL, properties);
399 : }
400 :
401 : /**
402 : * @brief Helper function to create ZoneoutLSTMCell layer
403 : */
404 : inline std::unique_ptr<Layer>
405 : ZoneoutLSTMCell(const std::vector<std::string> &properties = {}) {
406 : return createLayer(LayerType::LAYER_ZONEOUT_LSTMCELL, properties);
407 : }
408 :
409 : /**
410 : * @brief Helper function to create GRU layer
411 : */
412 : inline std::unique_ptr<Layer>
413 : GRU(const std::vector<std::string> &properties = {}) {
414 : return createLayer(LayerType::LAYER_GRU, properties);
415 : }
416 :
417 : /**
418 : * @brief Helper function to create GRUCell layer
419 : */
420 : inline std::unique_ptr<Layer>
421 : GRUCell(const std::vector<std::string> &properties = {}) {
422 : return createLayer(LayerType::LAYER_GRUCELL, properties);
423 : }
424 :
425 : /**
426 : * @brief Helper function to create DropOut layer
427 : */
428 : inline std::unique_ptr<Layer>
429 : DropOut(const std::vector<std::string> &properties = {}) {
430 : return createLayer(LayerType::LAYER_DROPOUT, properties);
431 : }
432 :
433 : /**
434 : * @brief Helper function to create Time Distributed layer
435 : */
436 : inline std::unique_ptr<Layer>
437 : TimeDistLayer(const std::vector<std::string> &properties = {}) {
438 : return createLayer(LayerType::LAYER_TIME_DIST, properties);
439 : }
440 :
441 : /**
442 : * @brief Helper function to create Centroid KNN Layer
443 : */
444 : inline std::unique_ptr<Layer>
445 : CentroidKNN(const std::vector<std::string> &properties = {}) {
446 : return createLayer(LayerType::LAYER_CENTROID_KNN, properties);
447 : }
448 :
449 : /**
450 : * @brief Helper function to create Attention Layer
451 : */
452 : inline std::unique_ptr<Layer>
453 : Attention(const std::vector<std::string> &properties = {}) {
454 : return createLayer(LayerType::LAYER_ATTENTION, properties);
455 : }
456 :
457 : /**
458 : * @brief Helper function to create MoL Attention Layer
459 : */
460 : inline std::unique_ptr<Layer>
461 : MoLAttention(const std::vector<std::string> &properties = {}) {
462 : return createLayer(LayerType::LAYER_MOL_ATTENTION, properties);
463 : }
464 :
465 : /**
466 : * @brief Helper function to create Multi Head Attention Layer
467 : */
468 : inline std::unique_ptr<Layer>
469 : MultiHeadAttention(const std::vector<std::string> &properties = {}) {
470 : return createLayer(LayerType::LAYER_MULTI_HEAD_ATTENTION, properties);
471 : }
472 :
473 : /**
474 : * @brief Helper function to create Positional Encoding Layer
475 : */
476 : inline std::unique_ptr<Layer>
477 : PositionalEncoding(const std::vector<std::string> &properties = {}) {
478 : return createLayer(LayerType::LAYER_POSITIONAL_ENCODING, properties);
479 : }
480 :
481 : /**
482 : * @brief Helper function to create Permute Layer
483 : */
484 : inline std::unique_ptr<Layer>
485 : Permute(const std::vector<std::string> &properties = {}) {
486 : return createLayer(LayerType::LAYER_PERMUTE, properties);
487 : }
488 :
489 : /**
490 : * @brief Helper function to create Reduce Mean Layer
491 : */
492 : inline std::unique_ptr<Layer>
493 : ReduceMean(const std::vector<std::string> &properties = {}) {
494 : return createLayer(LayerType::LAYER_REDUCE_MEAN, properties);
495 : }
496 :
497 : /**
498 : * @brief Helper function to create Identity layer
499 : */
500 : inline std::unique_ptr<Layer>
501 : Identity(const std::vector<std::string> &properties = {}) {
502 : return createLayer(LayerType::LAYER_IDENTITY, properties);
503 : }
504 :
505 : /**
506 : * @brief Helper function to create activation layer
507 : */
508 : inline std::unique_ptr<Layer>
509 4 : Activation(const std::string &act,
510 : const std::vector<std::string> &properties = {}) {
511 4 : std::vector<std::string> props(properties);
512 4 : props.push_back(act);
513 4 : return createLayer(LayerType::LAYER_ACTIVATION, props);
514 : }
515 :
516 : /**
517 : * @brief Helper function to create ReLU activation layer
518 : */
519 : inline std::unique_ptr<Layer>
520 1 : ReLU(const std::vector<std::string> &properties = {}) {
521 2 : return Activation("Activation=relu", properties);
522 : }
523 :
524 : /**
525 : * @brief Helper function to create swish activation layer
526 : */
527 : inline std::unique_ptr<Layer>
528 : Swish(const std::vector<std::string> &properties = {}) {
529 : return Activation("Activation=swish", properties);
530 : }
531 :
532 : /**
533 : * @brief Helper function to create gelu activation layer
534 : */
535 : inline std::unique_ptr<Layer>
536 : GeLU(const std::vector<std::string> &properties = {}) {
537 : return Activation("Activation=gelu", properties);
538 : }
539 :
540 : /**
541 : * @brief Helper function to create Tanh layer
542 : */
543 : inline std::unique_ptr<Layer>
544 1 : Tanh(const std::vector<std::string> &properties = {}) {
545 2 : return Activation("Activation=tanh", properties);
546 : }
547 :
548 : /**
549 : * @brief Helper function to create sigmoid layer
550 : */
551 : inline std::unique_ptr<Layer>
552 1 : Sigmoid(const std::vector<std::string> &properties = {}) {
553 2 : return Activation("Activation=sigmoid", properties);
554 : }
555 :
556 : /**
557 : * @brief Helper function to create softmax layer
558 : */
559 : inline std::unique_ptr<Layer>
560 1 : Softmax(const std::vector<std::string> &properties = {}) {
561 2 : return Activation("Activation=softmax", properties);
562 : }
563 :
564 : } // namespace layer
565 :
566 : namespace loss {
567 : /**
568 : * @brief Helper function to create mse layer
569 : */
570 : inline std::unique_ptr<Layer>
571 1 : MSE(const std::vector<std::string> &properties = {}) {
572 1 : return createLayer(LayerType::LAYER_LOSS_MSE, properties);
573 : }
574 :
575 : /**
576 : * @brief Helper function to create cross entropy with sigmoid layer
577 : */
578 : inline std::unique_ptr<Layer>
579 1 : CrossEntropySigmoid(const std::vector<std::string> &properties = {}) {
580 1 : return createLayer(LayerType::LAYER_LOSS_CROSS_ENTROPY_SIGMOID, properties);
581 : }
582 :
583 : /**
584 : * @brief Helper function to create cross entropy with softmax layer
585 : */
586 : inline std::unique_ptr<Layer>
587 1 : CrossEntropySoftmax(const std::vector<std::string> &properties = {}) {
588 1 : return createLayer(LayerType::LAYER_LOSS_CROSS_ENTROPY_SOFTMAX, properties);
589 : }
590 :
591 : } // namespace loss
592 :
593 : } // namespace train
594 : } // namespace ml
595 : #endif // __ML_TRAIN_LAYER_H__
|