[AppContext] Integrate layer-devel
[platform/core/ml/nntrainer.git] / nntrainer / layers / layer.cpp
1 /**
2  * Copyright (C) 2019 Samsung Electronics Co., Ltd. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *   http://www.apache.org/licenses/LICENSE-2.0
8  * Unless required by applicable law or agreed to in writing, software
9  * distributed under the License is distributed on an "AS IS" BASIS,
10  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11  * See the License for the specific language governing permissions and
12  * limitations under the License.
13  *
14  *
15  * @file        layer.cpp
16  * @date        04 December 2019
17  * @brief       This is Layers Classes for Neural Network
18  * @see         https://github.com/nnstreamer/nntrainer
19  * @author      Jijoong Moon <jijoong.moon@samsung.com>
20  * @bug         No known bugs except for NYI items
21  *
22  */
23 #include <ostream>
24 #include <sstream>
25
26 #include <layer_internal.h>
27 #include <layer_node.h>
28 #include <nntrainer_error.h>
29 #include <nntrainer_log.h>
30 #include <parse_util.h>
31 #include <util_func.h>
32
33 namespace nntrainer {
34
35 void LayerV1::setActivation(ActivationType acti) {
36   if (acti == ActivationType::ACT_UNKNOWN) {
37     throw std::invalid_argument("Error:have to specify activation function");
38   }
39   activation_type = acti;
40 }
41
42 int LayerV1::checkValidation() {
43   int status = ML_ERROR_NONE;
44
45   if (activation_type == ActivationType::ACT_UNKNOWN) {
46     ml_loge("Error: Have to set activation for this layer");
47     return ML_ERROR_INVALID_PARAMETER;
48   }
49
50   return status;
51 }
52
53 void LayerV1::setBatch(unsigned int batch) {
54   for (unsigned int idx = 0; idx < getNumInputs(); ++idx)
55     input_dim[idx].setTensorDim(0, batch);
56
57   for (unsigned int idx = 0; idx < getNumOutputs(); ++idx)
58     output_dim[idx].setTensorDim(0, batch);
59 }
60
61 std::vector<Tensor> LayerV1::getOutputs() {
62   std::vector<Tensor> ret;
63   for (unsigned int i = 0; i < getNumOutputs(); ++i) {
64     ret.push_back(net_hidden[i]->getVariableRef());
65   }
66   return ret;
67 }
68
69 std::vector<Tensor> LayerV1::getDerivatives() {
70   std::vector<Tensor> ret;
71   for (unsigned int i = 0; i < getNumInputs(); ++i) {
72     ret.push_back(net_input[i]->getGradientRef());
73   }
74   return ret;
75 }
76
77 void LayerV1::copy(std::shared_ptr<LayerV1> l) {
78   for (auto const &w : l->weights)
79     weights.push_back(w.clone());
80
81   this->input_dim = l->input_dim;
82   this->output_dim = l->output_dim;
83   this->activation_type = l->activation_type;
84   this->loss = l->loss;
85   this->weight_regularizer = l->weight_regularizer;
86   this->weight_regularizer_constant = l->weight_regularizer_constant;
87   this->weight_initializer = l->weight_initializer;
88   this->trainable = l->trainable;
89 }
90
91 sharedConstTensors LayerV1::forwarding_with_val(sharedConstTensors input,
92                                                 sharedConstTensors label,
93                                                 bool training) {
94
95   if (getNumInputs() != input.size()) {
96     std::stringstream ss;
97     ss << "Number of inputs mismatched, given: " << input.size()
98        << " expected: " << getNumInputs();
99     throw std::invalid_argument(ss.str().c_str());
100   }
101
102   for (unsigned int i = 0; i < getNumInputs(); ++i) {
103     net_input[i]->getVariableRef() = input[i]->clone();
104   }
105
106   if (!label.empty()) {
107     for (unsigned int i = 0; i < getNumOutputs(); ++i) {
108       net_hidden[i]->getGradientRef() = label[i]->clone();
109     }
110   }
111
112   forwarding(training);
113
114   nntrainer::sharedConstTensors out;
115
116   for (unsigned int i = 0; i < getNumOutputs(); ++i) {
117     out.push_back(MAKE_SHARED_TENSOR(net_hidden[i]->getVariable()));
118   }
119
120   return out;
121 }
122
123 sharedConstTensors LayerV1::backwarding_with_val(sharedConstTensors label) {
124
125   for (unsigned int i = 0; i < getNumOutputs(); ++i) {
126     net_hidden[i]->getGradientRef() = label[i]->clone();
127   }
128
129   backwarding();
130
131   nntrainer::sharedConstTensors out;
132
133   for (unsigned int i = 0; i < getNumInputs(); ++i) {
134     out.push_back(MAKE_SHARED_TENSOR(net_input[i]->getGradient()));
135   }
136
137   return out;
138 }
139
140 void LayerV1::read(std::ifstream &file) {
141   for (auto &weight : weights) {
142     weight.getVariableRef().read(file);
143   }
144 }
145
146 void LayerV1::save(std::ofstream &file) {
147   for (auto &weight : weights) {
148     weight.getVariableRef().save(file);
149   }
150 }
151
152 int LayerV1::setProperty(std::vector<std::string> values) {
153   int status = ML_ERROR_NONE;
154
155   try {
156     values = loadProperties(values, layer_props);
157   } catch (std::invalid_argument &e) {
158     ml_loge("parsing property failed, reason: %s", e.what());
159     return ML_ERROR_INVALID_PARAMETER;
160   }
161
162   /// @todo: deprecate this in favor of loadProperties
163   for (unsigned int i = 0; i < values.size(); ++i) {
164     std::string key;
165     std::string value;
166
167     status = getKeyValue(values[i], key, value);
168     NN_RETURN_STATUS();
169
170     unsigned int type = parseLayerProperty(key);
171
172     if (value.empty()) {
173       ml_logd("value is empty: key: %s, value: %s", key.c_str(), value.c_str());
174       return ML_ERROR_INVALID_PARAMETER;
175     }
176
177     try {
178       /// @note this calls derived setProperty if available
179       setProperty(static_cast<PropertyType>(type), value);
180     } catch (...) {
181       ml_logd("value or key is not valid, key: %s, value: %s", key.c_str(),
182               value.c_str());
183       return ML_ERROR_INVALID_PARAMETER;
184     }
185   }
186   return status;
187 }
188
189 void LayerV1::setProperty(const PropertyType type, const std::string &value) {
190   int status = ML_ERROR_NONE;
191
192   switch (type) {
193   case PropertyType::input_shape: {
194     if (getNumInputs() != 1) {
195       throw std::invalid_argument("input_shape keyword is only for one input");
196     }
197
198     TensorDim &in_dim = input_dim[0];
199     if (!value.empty()) {
200       unsigned int cache_batch_size = 1;
201       /** cache original value of batch size */
202       if (in_dim.batch()) {
203         cache_batch_size = in_dim.batch();
204         in_dim.batch(1);
205       }
206       status = in_dim.setTensorDim(value.c_str());
207       if (in_dim.batch() > 1) {
208         ml_logw("Batch size set with input dimension %d is ignored."
209                 "Set batchsize property for the model to update batchsize.",
210                 in_dim.batch());
211       }
212       /** set back to cache value of dimension */
213       in_dim.batch(cache_batch_size);
214       throw_status(status);
215     }
216   } break;
217   case PropertyType::activation:
218     if (!value.empty()) {
219       setActivation((ActivationType)parseType(value, TOKEN_ACTI));
220     }
221     break;
222   case PropertyType::weight_regularizer:
223     if (!value.empty()) {
224       weight_regularizer =
225         (WeightRegularizer)parseType(value, TOKEN_WEIGHT_REGULARIZER);
226       if (weight_regularizer == WeightRegularizer::UNKNOWN) {
227         throw std::invalid_argument("[Layer] Unknown Weight decay");
228       }
229     }
230     break;
231   case PropertyType::weight_regularizer_constant:
232     if (!value.empty()) {
233       status = setFloat(weight_regularizer_constant, value);
234       throw_status(status);
235     }
236     break;
237   case PropertyType::weight_initializer:
238     if (!value.empty()) {
239       weight_initializer =
240         (WeightInitializer)parseType(value, TOKEN_WEIGHT_INIT);
241     }
242     break;
243   case PropertyType::bias_initializer:
244     if (!value.empty()) {
245       bias_initializer = (WeightInitializer)parseType(value, TOKEN_WEIGHT_INIT);
246     }
247     break;
248   case PropertyType::trainable:
249     if (!value.empty()) {
250       status = setBoolean(trainable, value);
251       throw_status(status);
252     }
253     break;
254   default:
255     std::string msg =
256       "[Layer] Unknown Layer Property Key for value " + std::string(value);
257     throw exception::not_supported(msg);
258   }
259 }
260
261 template <typename T>
262 void LayerV1::printIfValid(std::ostream &out, const PropertyType type,
263                            const T target) {
264   try {
265     setProperty(type);
266   } catch (exception::not_supported &e) {
267     return;
268   }
269
270   out << propToStr(static_cast<unsigned int>(type)) << ": " << target
271       << std::endl;
272 }
273
274 void LayerV1::printShapeInfo(std::ostream &out) {
275   for (unsigned int idx = 0; idx < getNumInputs(); ++idx) {
276     out << "input " << input_dim[idx];
277     for (unsigned int i = 0; i < weights.size(); i++)
278       out << "inner" << i << " " << weightAt(i).getVariable().getDim();
279   }
280   for (unsigned int idx = 0; idx < getNumOutputs(); ++idx) {
281     out << "output " << output_dim[idx];
282   }
283 }
284
285 void LayerV1::printPropertiesMeta(std::ostream &out) {
286   printIfValid(
287     out, PropertyType::activation,
288     static_cast<std::underlying_type<ActivationType>::type>(activation_type));
289 }
290
291 void LayerV1::printProperties(std::ostream &out) {
292   out << "Trainable: " << trainable << std::endl;
293   printIfValid(out, PropertyType::weight_regularizer,
294                static_cast<int>(weight_regularizer));
295   printIfValid(out, PropertyType::weight_regularizer_constant,
296                weight_regularizer_constant);
297 }
298
299 void LayerV1::printMetric(std::ostream &out) {
300   if (loss > 0) {
301     out << "Weight regularization loss: " << loss;
302   }
303 }
304
305 void LayerV1::printPreset(std::ostream &out, PrintPreset preset) {
306   unsigned int flags = 0;
307   switch (preset) {
308   case PrintPreset::PRINT_ALL:
309     flags = PRINT_WEIGHTS | PRINT_METRIC;
310     /// fall through intended
311   case PrintPreset::PRINT_SUMMARY_META:
312     flags |= PRINT_PROP_META;
313     /// fall through intended
314   case PrintPreset::PRINT_SUMMARY:
315     flags |= PRINT_INST_INFO | PRINT_SHAPE_INFO | PRINT_PROP | PRINT_PROP_META;
316     break;
317   case PrintPreset::PRINT_NONE:
318     return;
319   default:
320     throw ::std::invalid_argument("undefined preset given");
321   }
322   print(out, flags);
323 }
324
325 void LayerV1::print(std::ostream &out, unsigned int flags) {
326   /** @todo properly move print to LayerNode */
327   if (flags & PRINT_INST_INFO) {
328     out << "===================";
329     // if (getName().empty())
330     //   printInstance(out, this);
331     // else
332     //   out << "<" << getName() << ">" << std::endl;
333
334     out << "Layer Type: " << getType() << std::endl;
335   }
336
337   if (flags & PRINT_SHAPE_INFO) {
338     out << "======shape information: " << std::endl;
339     printShapeInfo(out);
340   }
341
342   if (flags & PRINT_PROP_META) {
343     out << "======meta properties: " << std::endl;
344     printPropertiesMeta(out);
345   }
346
347   if (flags & PRINT_PROP) {
348     out << "======properties: " << std::endl;
349     printProperties(out);
350   }
351
352   if (flags & PRINT_WEIGHTS) {
353     out << "======weights: " << std::endl;
354     for (auto const &weight : weights) {
355       out << '[' << weight.getName() << ']' << std::endl;
356       out << weight.getVariable();
357     }
358   }
359
360   if (flags & PRINT_METRIC) {
361     out << "======metrics: " << std::endl;
362     printMetric(out);
363   }
364 };
365
366 std::shared_ptr<LayerV1> getLayerV1Devel(std::shared_ptr<ml::train::Layer> l) {
367   return std::static_pointer_cast<LayerNode>(l)->getObject();
368 }
369
370 } /* namespace nntrainer */