You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

how-to-implement-custom-layer-step-by-step.md 8.3 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. # step1 create a new empty class
  2. ```cpp
  3. // mylayer.h
  4. #include "layer.h"
  5. using namespace ncnn;
  6. // a new layer type called MyLayer
  7. class MyLayer : public Layer
  8. {
  9. };
  10. // mylayer.cpp
  11. #include "mylayer.h"
  12. DEFINE_LAYER_CREATOR(MyLayer)
  13. ```
  14. # step2 declare layer parameters and weights
  15. ```cpp
  16. // mylayer.h
  17. #include "layer.h"
  18. using namespace ncnn;
  19. class MyLayer : public Layer
  20. {
  21. private:
  22. int channels;// new code
  23. float gamma;// new code
  24. Mat weight;// new code
  25. };
  26. // mylayer.cpp
  27. #include "mylayer.h"
  28. DEFINE_LAYER_CREATOR(MyLayer)
  29. ```
  30. # step3 implement load functions for parameters and weights
  31. ```cpp
  32. // mylayer.h
  33. #include "layer.h"
  34. using namespace ncnn;
  35. class MyLayer : public Layer
  36. {
  37. public:
  38. virtual int load_param(const ParamDict& pd);// new code
  39. virtual int load_model(const ModelBin& mb);// new code
  40. private:
  41. int channels;
  42. float eps;
  43. Mat gamma_data;
  44. };
  45. // mylayer.cpp
  46. #include "mylayer.h"
  47. DEFINE_LAYER_CREATOR(MyLayer)
  48. // new routine for loading parameters
  49. int MyLayer::load_param(const ParamDict& pd)
  50. {
  51. // details about the relations with param file
  52. // https://github.com/Tencent/ncnn/wiki/param-and-model-file-structure
  53. //
  54. channels = pd.get(0, 0);// parse 0=<int value> entry, default value 0
  55. eps = pd.get(1, 0.001f);// parse 1=<float value> entry, default value 0.001f
  56. return 0;// return zero if success
  57. }
  58. // new routine for loading weights
  59. int MyLayer::load_model(const ModelBin& mb)
  60. {
  61. // details about the relations with model file
  62. // https://github.com/Tencent/ncnn/wiki/param-and-model-file-structure
  63. //
  64. // read weights with length of channels * sizeof(float)
  65. // the second argument explains as follows
  66. // 0 judge the value type automatically, you may get float or float16 or uint8 etc
  67. // depends on the model storage and the supporting target hardware
  68. // 1 read float values anyway
  69. // 2 read float16 values anyway
  70. // 3 read uint8 values anyway
  71. gamma_data = mb.load(channels, 1);
  72. if (gamma_data.empty())
  73. return -100;// return non-zero on error, -100 indicates out-of-memory
  74. return 0;// return zero if success
  75. }
  76. ```
  77. # step4 determine forward behavior
  78. ```cpp
  79. // mylayer.h
  80. #include "layer.h"
  81. using namespace ncnn;
  82. class MyLayer : public Layer
  83. {
  84. public:
  85. MyLayer();// new code
  86. virtual int load_param(const ParamDict& pd);
  87. virtual int load_model(const ModelBin& mb);
  88. private:
  89. int channels;
  90. float eps;
  91. Mat gamma_data;
  92. };
  93. // mylayer.cpp
  94. #include "mylayer.h"
  95. DEFINE_LAYER_CREATOR(MyLayer)
  96. // new routine for setting forward behavior
  97. MyLayer::MyLayer()
  98. {
  99. // one input and one output
  100. // typical one_blob_only type: Convolution, Pooling, ReLU, Softmax ...
  101. // typical non-one_blob_only type: Eltwise, Split, Concat, Slice ...
  102. one_blob_only = true;
  103. // do not change the blob size, modify data in-place
  104. // typical support_inplace type: ReLU, Sigmoid ...
  105. // typical non-support_inplace type: Convolution, Pooling ...
  106. support_inplace = true;
  107. }
  108. int MyLayer::load_param(const ParamDict& pd)
  109. {
  110. channels = pd.get(0, 0);
  111. eps = pd.get(1, 0.001f);
  112. // you could alter the behavior based on loaded parameter
  113. // if (eps == 0.001f)
  114. // {
  115. // one_blob_only = false;
  116. // support_inplace = false;
  117. // }
  118. return 0;
  119. }
  120. int MyLayer::load_model(const ModelBin& mb)
  121. {
  122. gamma_data = mb.load(channels, 1);
  123. if (gamma_data.empty())
  124. return -100;
  125. // you could alter the behavior based on loaded weight
  126. // if (gamma_data[0] == 0.f)
  127. // {
  128. // one_blob_only = false;
  129. // support_inplace = false;
  130. // }
  131. return 0;
  132. }
  133. ```
  134. # step5 choose proper interface based on forward behavior
  135. ```cpp
  136. // The base class Layer defines four interfaces for each forward behavior combination
  137. // 1
  138. virtual int forward(const std::vector<Mat>& bottom_blobs, std::vector<Mat>& top_blobs, const Option& opt) const;
  139. // 2
  140. virtual int forward(const Mat& bottom_blob, Mat& top_blob, const Option& opt) const;
  141. // 3
  142. virtual int forward_inplace(std::vector<Mat>& bottom_top_blobs, const Option& opt) const;
  143. // 4
  144. virtual int forward_inplace(Mat& bottom_top_blob, const Option& opt) const;
  145. ```
  146. **must** = layer must implement this function
  147. **optional** = layer may implement this function for optimal performance
  148. sometimes the graph inference path cannot call forward_inplace directly due to data sharing, in this situation the non-inplace forward routine will be used, which deep-copy the input blob and call inplace forward on it if the optional routine is not implemented. Thus, you could avoid this deep-copy by process input to output on-the-fly.
  149. |one_blob_only|support_inplace|1|2|3|4|
  150. |---|---|---|---|---|---|
  151. |false|false|must| | | |
  152. |false|true|optional| |must| |
  153. |true|false| |must| | |
  154. |true|true| |optional| |must|
  155. # step6 implement forward function
  156. ```cpp
  157. // mylayer.h
  158. #include "layer.h"
  159. using namespace ncnn;
  160. class MyLayer : public Layer
  161. {
  162. public:
  163. MyLayer();
  164. virtual int load_param(const ParamDict& pd);
  165. virtual int load_model(const ModelBin& mb);
  166. virtual int forward(const Mat& bottom_blob, Mat& top_blob, const Option& opt) const;// new code, optional
  167. virtual int forward_inplace(Mat& bottom_top_blob, const Option& opt) const;// new code
  168. private:
  169. int channels;
  170. float eps;
  171. Mat gamma_data;
  172. };
  173. // mylayer.cpp
  174. #include "mylayer.h"
  175. DEFINE_LAYER_CREATOR(MyLayer)
  176. MyLayer::MyLayer()
  177. {
  178. one_blob_only = true;
  179. support_inplace = true;
  180. }
  181. int MyLayer::load_param(const ParamDict& pd)
  182. {
  183. channels = pd.get(0, 0);
  184. eps = pd.get(1, 0.001f);
  185. return 0;
  186. }
  187. int MyLayer::load_model(const ModelBin& mb)
  188. {
  189. gamma_data = mb.load(channels, 1);
  190. if (gamma_data.empty())
  191. return -100;
  192. return 0;
  193. }
  194. // optional new routine for layer forward function, non-inplace version
  195. int MyLayer::forward(const Mat& bottom_blob, Mat& top_blob, const Option& opt) const
  196. {
  197. // check input dims, return non-zero on error
  198. if (bottom_blob.c != channels)
  199. return -1;
  200. // x = (x + eps) * gamma_per_channel
  201. int w = bottom_blob.w;
  202. int h = bottom_blob.h;
  203. size_t elemsize = bottom_blob.elemsize;
  204. int size = w * h;
  205. top_blob.create(w, h, channels, elemsize, opt.blob_allocator);
  206. if (top_blob.empty())
  207. return -100;// return non-zero on error, -100 indicates out-of-memory
  208. #pragma omp parallel for num_threads(opt.num_threads)
  209. for (int q=0; q<channels; q++)
  210. {
  211. const float* ptr = bottom_blob.channel(q);
  212. float* outptr = top_blob.channel(q);
  213. const float gamma = gamma_data[q];
  214. for (int i=0; i<size; i++)
  215. {
  216. outptr[i] = (ptr[i] + eps) * gamma ;
  217. }
  218. }
  219. return 0;
  220. }
  221. // new routine for layer forward function
  222. int MyLayer::forward_inplace(Mat& bottom_top_blob, const Option& opt) const
  223. {
  224. // check input dims, return non-zero on error
  225. if (bottom_top_blob.c != channels)
  226. return -1;
  227. // x = (x + eps) * gamma_per_channel
  228. int w = bottom_top_blob.w;
  229. int h = bottom_top_blob.h;
  230. int size = w * h;
  231. #pragma omp parallel for num_threads(opt.num_threads)
  232. for (int q=0; q<channels; q++)
  233. {
  234. float* ptr = bottom_top_blob.channel(q);
  235. const float gamma = gamma_data[q];
  236. for (int i=0; i<size; i++)
  237. {
  238. ptr[i] = (ptr[i] + eps) * gamma ;
  239. }
  240. }
  241. return 0;
  242. }
  243. ```
  244. # step7 integrate with ncnn library
  245. you may probably need to modify caffe2ncnn or mxnet2ncnn etc. to write your layer specific parameters and weights into ncnn param and model file
  246. the param and model file structure [param-and-model-file-structure](param-and-model-file-structure)
  247. ```
  248. // example param file content
  249. Input input 0 1 input
  250. Convolution conv2d 1 1 input conv2d 0=32 1=1 2=1 3=1 4=0 5=0 6=768
  251. MyLayer mylayer 1 1 conv2d mylayer0
  252. Pooling maxpool 1 1 mylayer0 maxpool 0=0 1=3 2=2 3=-233 4=0
  253. ```
  254. ```cpp
  255. ncnn::Net net;
  256. // register custom layer before load param and model
  257. // the layer creator function signature is always XYZ_layer_creator, which defined in DEFINE_LAYER_CREATOR macro
  258. net.register_custom_layer("MyLayer", MyLayer_layer_creator);
  259. net.load_param("model.param");
  260. net.load_model("model.bin");
  261. ```