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 common_properties.cpp
6 : * @date 14 May 2021
7 : * @brief This file contains implementation of common properties widely used
8 : * across layers
9 : * @see https://github.com/nnstreamer/nntrainer
10 : * @author Jihoon Lee <jhoon.it.lee@samsung.com>
11 : * @bug No known bugs except for NYI items
12 : */
13 : #include <base_properties.h>
14 : #include <common_properties.h>
15 :
16 : #include <nntrainer_error.h>
17 : #include <nntrainer_log.h>
18 : #include <stdexcept>
19 : #include <tensor_dim.h>
20 :
21 : #include <regex>
22 : #include <sstream>
23 : #include <sys/stat.h>
24 : #include <utility>
25 : #include <vector>
26 :
27 : namespace nntrainer {
28 : namespace props {
29 :
30 17071 : Name::Name() : nntrainer::Property<std::string>() {}
31 :
32 16421 : Name::Name(const std::string &value) { set(value); }
33 :
34 37735 : void Name::set(const std::string &value) {
35 37735 : auto to_lower = [](const std::string &str) {
36 : std::string ret = str;
37 : std::transform(ret.begin(), ret.end(), ret.begin(),
38 494929 : [](unsigned char c) { return std::tolower(c); });
39 37735 : return ret;
40 : };
41 37735 : nntrainer::Property<std::string>::set(to_lower(value));
42 37710 : }
43 :
44 37735 : bool Name::isValid(const std::string &v) const {
45 37735 : static std::regex allowed("[a-zA-Z0-9][-_./a-zA-Z0-9]*");
46 75493 : return !v.empty() && std::regex_match(v, allowed);
47 : }
48 :
49 1432 : Normalization::Normalization(bool value) { set(value); }
50 :
51 1432 : Standardization::Standardization(bool value) { set(value); }
52 :
53 216 : bool DropOutRate::isValid(const float &v) const { return v >= 0.0; }
54 :
55 0 : void RandomTranslate::set(const float &value) {
56 0 : Property<float>::set(std::abs(value));
57 0 : }
58 :
59 220 : bool FilePath::isValid(const std::string &v) const {
60 220 : std::ifstream file(v, std::ios::binary | std::ios::ate);
61 220 : return file.good();
62 220 : }
63 :
64 220 : void FilePath::set(const std::string &v) {
65 220 : Property<std::string>::set(v);
66 218 : std::ifstream file(v, std::ios::binary | std::ios::ate);
67 218 : cached_pos_size = file.tellg();
68 218 : }
69 :
70 100 : std::ifstream::pos_type FilePath::file_size() { return cached_pos_size; }
71 :
72 6178 : bool LossScaleForMixed::isValid(const float &value) const {
73 6178 : return (value != 0);
74 : }
75 :
76 0 : bool DirPath::isValid(const std::string &v) const {
77 : struct stat dir;
78 0 : return (stat(v.c_str(), &dir) == 0);
79 : }
80 :
81 0 : void DirPath::set(const std::string &v) { Property<std::string>::set(v); }
82 :
83 209 : ReturnSequences::ReturnSequences(bool value) { set(value); }
84 :
85 76 : Bidirectional::Bidirectional(bool value) { set(value); }
86 :
87 4 : bool NumClass::isValid(const unsigned int &v) const { return v > 0; }
88 :
89 6120 : InputConnection::InputConnection() : nntrainer::Property<Connection>() {}
90 60 : InputConnection::InputConnection(const Connection &value) :
91 60 : nntrainer::Property<Connection>(value) {} /**< default value if any */
92 :
93 250 : Epsilon::Epsilon(float value) { set(value); }
94 :
95 40 : Exponent::Exponent(float value) { set(value); }
96 :
97 421 : bool Epsilon::isValid(const float &value) const { return value > 0.0f; }
98 :
99 58 : Momentum::Momentum(float value) { set(value); }
100 :
101 81 : bool Momentum::isValid(const float &value) const {
102 81 : return value > 0.0f && value < 1.0f;
103 : }
104 :
105 180 : bool Axis::isValid(const unsigned int &value) const {
106 180 : return value < ml::train::TensorDim::MAXDIM;
107 : }
108 :
109 160 : StartDimension::StartDimension(unsigned int value) { set(value); }
110 :
111 168 : bool StartDimension::isValid(const unsigned int &value) const {
112 168 : return value > 0 && value < ml::train::TensorDim::MAXDIM;
113 : }
114 :
115 160 : EndDimension::EndDimension(unsigned int value) { set(value); }
116 :
117 164 : bool EndDimension::isValid(const unsigned int &value) const {
118 164 : return value > 0 && value < ml::train::TensorDim::MAXDIM;
119 : }
120 :
121 62 : bool SplitDimension::isValid(const unsigned int &value) const {
122 62 : return value > 0 && value < ml::train::TensorDim::MAXDIM;
123 : }
124 :
125 36 : PoolSize::PoolSize(unsigned int value) { set(value); }
126 :
127 1010 : Stride::Stride(unsigned int value) { set(value); }
128 :
129 548 : Dilation::Dilation(unsigned int value) { set(value); }
130 :
131 : /**
132 : * @brief unsigned integer property, internally used to parse padding values
133 : *
134 : */
135 1412 : class Padding_ : public nntrainer::Property<int> {
136 : public:
137 : using prop_tag = int_prop_tag; /**< property type */
138 : };
139 :
140 196 : bool Padding2D::isValid(const std::string &v) const {
141 :
142 : /// case 1, 2: padding has string literal
143 372 : if (istrequal(v, "valid") || istrequal(v, "same")) {
144 : return true;
145 : }
146 :
147 : std::vector<props::Padding_> paddings;
148 148 : from_string(v, paddings);
149 :
150 : /// case 3, 4, 5: padding has a sequence of unsigned integer
151 : if (paddings.size() == 1 || paddings.size() == 2 || paddings.size() == 4) {
152 : /// check if every padding is non-negative integer
153 491 : for (const auto &padding : paddings) {
154 348 : if (padding.get() < 0) {
155 : return false;
156 : }
157 : }
158 : return true;
159 : }
160 :
161 : /// case else: false
162 : return false;
163 148 : }
164 :
165 : std::array<unsigned int, 4>
166 219 : Padding2D::compute(const TensorDim &input, const TensorDim &kernel,
167 : const std::array<unsigned int, 2> &strides,
168 : const std::array<unsigned int, 2> &dilation) {
169 219 : auto &padding_repr = get(); /// padding representation
170 :
171 438 : if (istrequal(padding_repr, "valid")) {
172 54 : return {0, 0, 0, 0};
173 : }
174 :
175 : /// in the case of same padding, padding is distributed to each side if
176 : /// possible. otherwise pad_all_side / 2 is allocated to top | left and rest
177 : /// are assigned to the other side
178 330 : if (istrequal(padding_repr, "same")) {
179 : auto calculate_padding = [](unsigned input_, unsigned kernel_,
180 : unsigned stride, unsigned dilation) {
181 : /// ceil(input / stride)
182 54 : unsigned int eff_kernel = (kernel_ - 1) * dilation + 1;
183 54 : auto out = (input_ + stride - 1) / stride;
184 54 : auto req_input = (out - 1) * stride + eff_kernel;
185 54 : return req_input >= input_ ? req_input - input_ : 0;
186 : };
187 :
188 27 : auto pad_vertical = calculate_padding(input.height(), kernel.height(),
189 : strides[0], dilation[0]);
190 : auto pad_horizontal =
191 27 : calculate_padding(input.width(), kernel.width(), strides[1], dilation[1]);
192 :
193 27 : auto pad_top = pad_vertical / 2;
194 27 : auto pad_left = pad_horizontal / 2;
195 :
196 27 : return {pad_top, pad_vertical - pad_top, pad_left,
197 27 : pad_horizontal - pad_left};
198 : }
199 :
200 : /// case 3, 4, 5: padding has a sequence of unsigned integer
201 : std::vector<props::Padding_> paddings_;
202 138 : from_string(padding_repr, paddings_);
203 138 : std::vector<unsigned int> paddings(paddings_.begin(), paddings_.end());
204 :
205 138 : switch (paddings.size()) {
206 : case 1:
207 5 : return {paddings[0], paddings[0], paddings[0], paddings[0]};
208 : case 2:
209 100 : return {paddings[0], paddings[0], paddings[1], paddings[1]};
210 : case 4:
211 33 : return {paddings[0], paddings[1], paddings[2], paddings[3]};
212 0 : default:
213 0 : throw std::logic_error("[Padding2D] should not reach here");
214 : }
215 138 : }
216 :
217 18 : bool Padding1D::isValid(const std::string &v) const {
218 :
219 : /// case 1, 2, 3: padding has string literal
220 44 : if (istrequal(v, "valid") || istrequal(v, "same") || istrequal(v, "causal")) {
221 : return true;
222 : }
223 :
224 : std::vector<props::Padding_> paddings;
225 6 : from_string(v, paddings);
226 :
227 : /// case 4, 5: padding has a sequence of unsigned integer
228 6 : if (paddings.size() == 1 || paddings.size() == 2) {
229 : /// check if every padding is non-negative integer
230 14 : for (const auto &padding : paddings) {
231 8 : if (padding.get() < 0) {
232 : return false;
233 : }
234 : }
235 : return true;
236 : }
237 :
238 : /// case else: false
239 : return false;
240 6 : }
241 :
242 26 : std::array<unsigned int, 2> Padding1D::compute(const TensorDim &input_dim,
243 : const unsigned int &kernel,
244 : const unsigned int &stride,
245 : const unsigned int &dilation) {
246 26 : auto &padding_repr = get(); /// padding representation
247 :
248 : auto calculate_padding = [](unsigned input, unsigned kernel, unsigned stride,
249 : unsigned dilation) {
250 : /// ceil(input / stride)
251 10 : unsigned int eff_kernel = (kernel - 1) * dilation + 1;
252 10 : auto out = (input + stride - 1) / stride;
253 10 : auto req_input = (out - 1) * stride + eff_kernel;
254 10 : return req_input >= input ? req_input - input : 0;
255 : };
256 :
257 52 : if (istrequal(padding_repr, "valid")) {
258 10 : return {0, 0};
259 32 : } else if (istrequal(padding_repr, "same")) {
260 :
261 : auto pad_horizontal =
262 6 : calculate_padding(input_dim.width(), kernel, stride, dilation);
263 :
264 6 : auto pad_left = pad_horizontal / 2;
265 :
266 6 : return {pad_left, pad_horizontal - pad_left};
267 20 : } else if (istrequal(padding_repr, "causal")) {
268 : auto pad_horizontal =
269 4 : calculate_padding(input_dim.width(), kernel, stride, dilation);
270 4 : return {pad_horizontal, 0};
271 : }
272 :
273 : /// case 4, 5: padding has a sequence of unsigned integer
274 : std::vector<props::Padding_> paddings_;
275 6 : from_string(padding_repr, paddings_);
276 6 : std::vector<unsigned int> paddings(paddings_.begin(), paddings_.end());
277 :
278 6 : switch (paddings.size()) {
279 : case 1:
280 4 : return {paddings[0], paddings[0]};
281 : case 2:
282 2 : return {paddings[0], paddings[1]};
283 0 : default:
284 0 : throw std::logic_error("[Padding1D] should not reach here");
285 : }
286 6 : }
287 :
288 6749 : BasicRegularizerConstant::BasicRegularizerConstant(float value) { set(value); }
289 :
290 2086 : WeightRegularizerConstant::WeightRegularizerConstant(float value) :
291 2086 : BasicRegularizerConstant(value) {}
292 2331 : WeightDecay::WeightDecay(float value) : BasicRegularizerConstant(value) {}
293 2331 : BiasDecay::BiasDecay(float value) : BasicRegularizerConstant(value) {}
294 :
295 1286 : PropsUserData::PropsUserData(void *user_data) { set(user_data); }
296 :
297 9167 : bool BasicRegularizerConstant::isValid(const float &value) const {
298 9167 : return value >= 0.0f;
299 : }
300 :
301 0 : OutputLayer::OutputLayer() : Name() {}
302 0 : OutputLayer::OutputLayer(const std::string &name) : Name(name) {}
303 :
304 84 : LabelLayer::LabelLayer() : Name() {}
305 0 : LabelLayer::LabelLayer(const std::string &name) : Name(name) {}
306 :
307 1324 : HiddenStateActivation::HiddenStateActivation(ActivationTypeInfo::Enum value) {
308 1324 : set(value);
309 1324 : };
310 :
311 1132 : RecurrentActivation::RecurrentActivation(ActivationTypeInfo::Enum value) {
312 1132 : set(value);
313 1132 : };
314 :
315 2086 : WeightInitializer::WeightInitializer(Initializer value) { set(value); }
316 :
317 2086 : BiasInitializer::BiasInitializer(Initializer value) { set(value); }
318 :
319 56 : MuInitializer::MuInitializer(Initializer value) { set(value); }
320 :
321 56 : VarInitializer::VarInitializer(Initializer value) { set(value); }
322 :
323 245 : GammaInitializer::GammaInitializer(Initializer value) { set(value); }
324 :
325 245 : BetaInitializer::BetaInitializer(Initializer value) { set(value); }
326 :
327 2087 : BasicRegularizer::BasicRegularizer(nntrainer::WeightRegularizer value) {
328 2087 : set(value);
329 2086 : }
330 :
331 2086 : WeightRegularizer::WeightRegularizer(nntrainer::WeightRegularizer value) :
332 2086 : BasicRegularizer(value) {}
333 :
334 2840 : bool BasicRegularizer::isValid(
335 : const nntrainer::WeightRegularizer &value) const {
336 2840 : return value != nntrainer::WeightRegularizer::UNKNOWN;
337 : }
338 :
339 33 : FlipDirection::FlipDirection(FlipDirectionInfo::Enum value) { set(value); }
340 :
341 1861 : void GenericShape::set(const TensorDim &value) {
342 1861 : TensorDim ret = value;
343 1861 : ret.setDynDimFlag(0b1000);
344 1861 : if (ret.batch() != 1) {
345 23 : ml_logw("Batch size set with dimension %zu is ignored."
346 : "Use batchsize property for the model to update batchsize.",
347 : ret.batch());
348 23 : ret.batch(1);
349 : }
350 1861 : Property<TensorDim>::set(ret);
351 1861 : }
352 :
353 21 : ScaledDotProduct::ScaledDotProduct(bool value) { set(value); }
354 :
355 21 : CausalMask::CausalMask(bool value) { set(value); }
356 :
357 149 : NumHeads::NumHeads(unsigned int value) { set(value); }
358 :
359 149 : ReturnAttentionWeight::ReturnAttentionWeight(
360 149 : ReturnAttentionWeightInfo::Enum value) {
361 149 : set(value);
362 149 : }
363 :
364 : } // namespace props
365 :
366 : template <>
367 3199 : std::string str_converter<props::connection_prop_tag, Connection>::to_string(
368 : const Connection &value) {
369 3199 : return value.toString();
370 : }
371 :
372 : template <>
373 6562 : Connection str_converter<props::connection_prop_tag, Connection>::from_string(
374 : const std::string &value) {
375 6562 : return Connection(value);
376 : }
377 :
378 : } // namespace nntrainer
|