1# NCNN增加自定义层
2
3## 举例
4
5这里举个例子添加自定义层次 如Relu6,即 std::min(6, std::max(0, val))
6
7```
8Input            input   0 1 input
9Convolution      conv2d  1 1 input conv2d 0=32 1=1 2=1 3=1 4=0 5=0 6=768
10Relu6            relu6   1 1 conv2d relu6
11Pooling          maxpool 1 1 relu6 maxpool 0=0 1=3 2=2 3=-233 4=0
12```
13
14
15
16## 定义源码h文件:src/layer/relu6.h
17
18```CPP
19#ifndef LAYER_RELU6_H
20#define LAYER_RELU6_H
21
22#include "layer.h"
23
24namespace ncnn {
25
26class Relu6 : public Layer
27{
28public:
29    Relu6();
30
31    virtual int forward_inplace(Mat& bottom_top_blob, const Option& opt) const;
32};
33
34} // namespace ncnn
35
36#endif // LAYER_RELU6_H
37```
38
39
40
41## 定义源码CPP文件:src/layer/relu6.cpp
42
43```CPP
44#include "relu6.h"
45
46#include <math.h>
47
48namespace ncnn {
49
50Relu6::Relu6()
51{
52    one_blob_only = true;
53    support_inplace = true;
54}
55
56int Relu6::forward_inplace(Mat& bottom_top_blob, const Option& opt) const
57{
58        int w = bottom_top_blob.w;
59        int h = bottom_top_blob.h;
60        int channels = bottom_top_blob.c;
61        int size = w * h;
62
63        #pragma omp parallel for num_threads(opt.num_threads)
64        for (int q=0; q < channels; q++)
65        {
66            float* ptr = bottom_top_blob.channel(q);
67
68            for (int i=0; i<size; i++)
69            {
70                ptr[i] = std::min(6, std::max(0, ptr[i]));
71            }
72        }
73
74        return 0;
75}
76
77} // namespace ncnn
78
79```
80
81
82
83## 修改 src/CMakeLists.txt 注册Relu6
84
85```CPP
86ncnn_add_layer(GroupNorm)
87ncnn_add_layer(LayerNorm)
88ncnn_add_layer(Relu6)
89```
90
91
92
93## 定义测试用例CPP文件 src/test_relu6.cpp
94
95```CPP
96#include "layer/relu6.h"
97#include "testutil.h"
98
99static int test_relu6(const ncnn::Mat& a)
100{
101    ncnn::ParamDict pd;
102
103    std::vector<ncnn::Mat> weights(0);
104
105    int ret = test_layer<ncnn::Relu6>("Relu6", pd, weights, a);
106    if (ret != 0)
107    {
108        fprintf(stderr, "test_relu6 failed a.dims=%d a=(%d %d %d)\n", a.dims, a.w, a.h, a.c);
109    }
110
111    return ret;
112}
113
114static int test_relu6_0()
115{
116    return 0
117           || test_relu6(RandomMat(5, 7, 24))
118           || test_relu6(RandomMat(7, 9, 12))
119           || test_relu6(RandomMat(3, 5, 13));
120}
121
122static int test_relu6_1()
123{
124    return 0
125           || test_relu6(RandomMat(15, 24))
126           || test_relu6(RandomMat(17, 12))
127           || test_relu6(RandomMat(19, 15));
128}
129
130static int test_relu6_2()
131{
132    return 0
133           || test_relu6(RandomMat(128))
134           || test_relu6(RandomMat(124))
135           || test_relu6(RandomMat(127));
136}
137
138int main()
139{
140    SRAND(7767517);
141
142    return 0
143           || test_relu6_0()
144           || test_relu6_1()
145           || test_relu6_2();
146}
147
148```
149
150
151
152## 修改tests/CMakeLists.txt 注册Relu6测试用例
153
154```CPP
155ncnn_add_layer_test(LSTM)
156ncnn_add_layer_test(Yolov3DetectionOutput)
157ncnn_add_layer_test(Relu6)
158```
159
160
161
162## 编译
163
164```
165按原NCNN步骤编译
166```
167
168
169
170## 单元测试
171
172```
173./test_relu6
174```
175
176