tiny_dnn  1.0.0
A header only, dependency-free deep learning framework in C++11
util.h
1 /*
2  Copyright (c) 2013, Taiga Nomi
3  All rights reserved.
4 
5  Redistribution and use in source and binary forms, with or without
6  modification, are permitted provided that the following conditions are met:
7  * Redistributions of source code must retain the above copyright
8  notice, this list of conditions and the following disclaimer.
9  * Redistributions in binary form must reproduce the above copyright
10  notice, this list of conditions and the following disclaimer in the
11  documentation and/or other materials provided with the distribution.
12  * Neither the name of the <organization> nor the
13  names of its contributors may be used to endorse or promote products
14  derived from this software without specific prior written permission.
15 
16  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
17  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27 #pragma once
28 #include <vector>
29 #include <functional>
30 #include <random>
31 #include <type_traits>
32 #include <limits>
33 #include <cassert>
34 #include <cstdio>
35 #include <cstdarg>
36 #include <string>
37 #include <sstream>
38 
39 #include <cereal/cereal.hpp>
40 #include <cereal/archives/json.hpp>
41 #include <cereal/archives/binary.hpp>
42 #include <cereal/types/string.hpp>
43 #include <cereal/types/vector.hpp>
44 #include <cereal/types/deque.hpp>
45 
46 #include "tiny_dnn/config.h"
47 #include "tiny_dnn/util/macro.h"
48 #include "tiny_dnn/util/aligned_allocator.h"
49 #include "tiny_dnn/util/nn_error.h"
50 #include "tiny_dnn/util/parallel_for.h"
51 #include "tiny_dnn/util/random.h"
52 
53 #if defined(USE_OPENCL) || defined(USE_CUDA)
54 #ifdef USE_OPENCL
55 #include "third_party/CLCudaAPI/clpp11.h"
56 #else
57 #include "third_party/CLCudaAPI/cupp11.h"
58 #endif
59 #endif
60 
61 namespace tiny_dnn {
62 
65 typedef serial_size_t label_t;
66 
67 typedef serial_size_t layer_size_t; // for backward compatibility
68 
69 typedef std::vector<float_t, aligned_allocator<float_t, 64>> vec_t;
70 
71 typedef std::vector<vec_t> tensor_t;
72 
73 enum class net_phase {
74  train,
75  test
76 };
77 
78 enum class padding {
79  valid,
80  same
81 };
82 
83 template<typename T>
84 T* reverse_endian(T* p) {
85  std::reverse(reinterpret_cast<char*>(p), reinterpret_cast<char*>(p) + sizeof(T));
86  return p;
87 }
88 
89 inline bool is_little_endian() {
90  int x = 1;
91  return *(char*) &x != 0;
92 }
93 
94 
95 template<typename T>
96 size_t max_index(const T& vec) {
97  auto begin_iterator = std::begin(vec);
98  return std::max_element(begin_iterator, std::end(vec)) - begin_iterator;
99 }
100 
101 template<typename T, typename U>
102 U rescale(T x, T src_min, T src_max, U dst_min, U dst_max) {
103  U value = static_cast<U>(((x - src_min) * (dst_max - dst_min)) / (src_max - src_min) + dst_min);
104  return std::min(dst_max, std::max(value, dst_min));
105 }
106 
107 inline void nop()
108 {
109  // do nothing
110 }
111 
112 template <typename T> inline T sqr(T value) { return value*value; }
113 
114 inline bool isfinite(float_t x) {
115  return x == x;
116 }
117 
118 template <typename Container> inline bool has_infinite(const Container& c) {
119  for (auto v : c)
120  if (!isfinite(v)) return true;
121  return false;
122 }
123 
124 template <typename Container>
125 serial_size_t max_size(const Container& c) {
126  typedef typename Container::value_type value_t;
127  const auto max_size = std::max_element(c.begin(), c.end(),
128  [](const value_t& left, const value_t& right) { return left.size() < right.size(); })->size();
129  assert(max_size <= std::numeric_limits<serial_size_t>::max());
130  return static_cast<serial_size_t>(max_size);
131 }
132 
133 inline std::string format_str(const char *fmt, ...) {
134  static char buf[2048];
135 
136 #ifdef _MSC_VER
137 #pragma warning(disable:4996)
138 #endif
139  va_list args;
140  va_start(args, fmt);
141  vsnprintf(buf, sizeof(buf), fmt, args);
142  va_end(args);
143 #ifdef _MSC_VER
144 #pragma warning(default:4996)
145 #endif
146  return std::string(buf);
147 }
148 
149 template <typename T>
150 struct index3d {
151  index3d(T width, T height, T depth) {
152  reshape(width, height, depth);
153  }
154 
155  index3d() : width_(0), height_(0), depth_(0) {}
156 
157  void reshape(T width, T height, T depth) {
158  width_ = width;
159  height_ = height;
160  depth_ = depth;
161 
162  if ((long long) width * height * depth > std::numeric_limits<T>::max())
163  throw nn_error(
164  format_str("error while constructing layer: layer size too large for tiny-dnn\nWidthxHeightxChannels=%dx%dx%d >= max size of [%s](=%d)",
165  width, height, depth, typeid(T).name(), std::numeric_limits<T>::max()));
166  }
167 
168  T get_index(T x, T y, T channel) const {
169  assert(x >= 0 && x < width_);
170  assert(y >= 0 && y < height_);
171  assert(channel >= 0 && channel < depth_);
172  return (height_ * channel + y) * width_ + x;
173  }
174 
175  T area() const {
176  return width_ * height_;
177  }
178 
179  T size() const {
180  return width_ * height_ * depth_;
181  }
182 
183  template <class Archive>
184  void serialize(Archive & ar) {
185  ar(cereal::make_nvp("width", width_));
186  ar(cereal::make_nvp("height", height_));
187  ar(cereal::make_nvp("depth", depth_));
188  }
189 
190  T width_;
191  T height_;
192  T depth_;
193 };
194 
196 
197 template <typename T>
198 bool operator == (const index3d<T>& lhs, const index3d<T>& rhs) {
199  return (lhs.width_ == rhs.width_) && (lhs.height_ == rhs.height_) && (lhs.depth_ == rhs.depth_);
200 }
201 
202 template <typename T>
203 bool operator != (const index3d<T>& lhs, const index3d<T>& rhs) {
204  return !(lhs == rhs);
205 }
206 
207 template <typename Stream, typename T>
208 Stream& operator << (Stream& s, const index3d<T>& d) {
209  s << d.width_ << "x" << d.height_ << "x" << d.depth_;
210  return s;
211 }
212 
213 template <typename T>
214 std::ostream& operator << (std::ostream& s, const index3d<T>& d) {
215  s << d.width_ << "x" << d.height_ << "x" << d.depth_;
216  return s;
217 }
218 
219 template <typename Stream, typename T>
220 Stream& operator << (Stream& s, const std::vector<index3d<T>>& d) {
221  s << "[";
222  for (serial_size_t i = 0; i < d.size(); i++) {
223  if (i) s << ",";
224  s << "[" << d[i] << "]";
225  }
226  s << "]";
227  return s;
228 }
229 
230 // equivalent to std::to_string, which android NDK doesn't support
231 template <typename T>
232 std::string to_string(T value) {
233  std::ostringstream os;
234  os << value;
235  return os.str();
236 }
237 
238 // boilerplate to resolve dependent name
239 #define CNN_USE_LAYER_MEMBERS using layer::parallelize_; \
240  using feedforward_layer<Activation>::h_
241 
242 
243 #define CNN_LOG_VECTOR(vec, name)
244 /*
245 void CNN_LOG_VECTOR(const vec_t& vec, const std::string& name) {
246  std::cout << name << ",";
247 
248  if (vec.empty()) {
249  std::cout << "(empty)" << std::endl;
250  }
251  else {
252  for (size_t i = 0; i < vec.size(); i++) {
253  std::cout << vec[i] << ",";
254  }
255  }
256 
257  std::cout << std::endl;
258 }
259 */
260 
261 
262 template <typename T, typename Pred, typename Sum>
263 serial_size_t sumif(const std::vector<T>& vec, Pred p, Sum s) {
264  serial_size_t sum = 0;
265  for (serial_size_t i = 0; i < static_cast<serial_size_t>(vec.size()); i++) {
266  if (p(i)) sum += s(vec[i]);
267  }
268  return sum;
269 }
270 
271 template <typename T, typename Pred>
272 std::vector<T> filter(const std::vector<T>& vec, Pred p) {
273  std::vector<T> res;
274  for (size_t i = 0; i < vec.size(); i++) {
275  if (p(i)) res.push_back(vec[i]);
276  }
277  return res;
278 }
279 
280 template <typename Result, typename T, typename Pred>
281 std::vector<Result> map_(const std::vector<T>& vec, Pred p) {
282  std::vector<Result> res;
283  for (auto& v : vec) {
284  res.push_back(p(v));
285  }
286  return res;
287 }
288 
289 enum class vector_type : int32_t {
290  // 0x0001XXX : in/out data
291  data = 0x0001000, // input/output data, fed by other layer or input channel
292 
293  // 0x0002XXX : trainable parameters, updated for each back propagation
294  weight = 0x0002000,
295  bias = 0x0002001,
296 
297  label = 0x0004000,
298  aux = 0x0010000 // layer-specific storage
299 };
300 
301 inline std::string to_string(vector_type vtype) {
302  switch (vtype)
303  {
304  case tiny_dnn::vector_type::data:
305  return "data";
306  case tiny_dnn::vector_type::weight:
307  return "weight";
308  case tiny_dnn::vector_type::bias:
309  return "bias";
310  case tiny_dnn::vector_type::label:
311  return "label";
312  case tiny_dnn::vector_type::aux:
313  return "aux";
314  default:
315  return "unknown";
316  }
317 }
318 
319 inline std::ostream& operator << (std::ostream& os, vector_type vtype) {
320  os << to_string(vtype);
321  return os;
322 }
323 
324 inline vector_type operator & (vector_type lhs, vector_type rhs) {
325  return (vector_type)(static_cast<int32_t>(lhs) & static_cast<int32_t>(rhs));
326 }
327 
328 inline bool is_trainable_weight(vector_type vtype) {
329  return (vtype & vector_type::weight) == vector_type::weight;
330 }
331 
332 inline std::vector<vector_type> std_input_order(bool has_bias) {
333  if (has_bias) {
334  return{ vector_type::data, vector_type::weight, vector_type::bias };
335  }
336  else {
337  return{ vector_type::data, vector_type::weight };
338  }
339 }
340 
341 inline std::vector<vector_type> std_output_order(bool has_activation) {
342  if (has_activation) {
343  return{ vector_type::data, vector_type::aux };
344  }
345  else {
346  return{ vector_type::data };
347  }
348 }
349 
350 inline void fill_tensor(tensor_t& tensor, float_t value) {
351  for (auto& t : tensor) {
352  std::fill(t.begin(), t.end(), value);
353  }
354 }
355 
356 inline void fill_tensor(tensor_t& tensor, float_t value, serial_size_t size) {
357  for (auto& t : tensor) {
358  t.resize(size, value);
359  }
360 }
361 
362 inline serial_size_t conv_out_length(serial_size_t in_length,
363  serial_size_t window_size,
364  serial_size_t stride,
365  padding pad_type) {
366  serial_size_t output_length;
367 
368  if (pad_type == padding::same) {
369  output_length = in_length;
370  }
371  else if (pad_type == padding::valid) {
372  output_length = in_length - window_size + 1;
373  }
374  else {
375  throw nn_error("Not recognized pad_type.");
376  }
377  return (output_length + stride - 1) / stride;
378 }
379 
380 // get all platforms (drivers), e.g. NVIDIA
381 // https://github.com/CNugteren/CLCudaAPI/blob/master/samples/device_info.cc
382 
383 inline void printAvailableDevice(const serial_size_t platform_id,
384  const serial_size_t device_id) {
385 #if defined(USE_OPENCL) || defined(USE_CUDA)
386  // Initializes the CLCudaAPI platform and device. This initializes the OpenCL/CUDA back-end and
387  // selects a specific device on the platform.
388  auto platform = CLCudaAPI::Platform(platform_id);
389  auto device = CLCudaAPI::Device(platform, device_id);
390 
391  // Prints information about the chosen device. Most of these results should stay the same when
392  // switching between the CUDA and OpenCL back-ends.
393  printf("\n## Printing device information...\n");
394  printf(" > Platform ID %zu\n", platform_id);
395  printf(" > Device ID %zu\n", device_id);
396  printf(" > Framework version %s\n", device.Version().c_str());
397  printf(" > Vendor %s\n", device.Vendor().c_str());
398  printf(" > Device name %s\n", device.Name().c_str());
399  printf(" > Device type %s\n", device.Type().c_str());
400  printf(" > Max work-group size %zu\n", device.MaxWorkGroupSize());
401  printf(" > Max thread dimensions %zu\n", device.MaxWorkItemDimensions());
402  printf(" > Max work-group sizes:\n");
403  for (auto i=size_t{0}; i<device.MaxWorkItemDimensions(); ++i) {
404  printf(" - in the %zu-dimension %zu\n", i, device.MaxWorkItemSizes()[i]);
405  }
406  printf(" > Local memory per work-group %zu bytes\n", device.LocalMemSize());
407  printf(" > Device capabilities %s\n", device.Capabilities().c_str());
408  printf(" > Core clock rate %zu MHz\n", device.CoreClock());
409  printf(" > Number of compute units %zu\n", device.ComputeUnits());
410  printf(" > Total memory size %zu bytes\n", device.MemorySize());
411  printf(" > Maximum allocatable memory %zu bytes\n", device.MaxAllocSize());
412  printf(" > Memory clock rate %zu MHz\n", device.MemoryClock());
413  printf(" > Memory bus width %zu bits\n", device.MemoryBusWidth());
414 #else
415  nn_warn("TinyDNN was not build with OpenCL or CUDA support.");
416 #endif
417 }
418 
419 template<typename T, typename... Args>
420 std::unique_ptr<T> make_unique(Args&&... args)
421 {
422  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
423 }
424 
425 } // namespace tiny_dnn
error exception class for tiny-dnn
Definition: nn_error.h:37
Definition: util.h:150