/home/liu/actions-runner/_work/ccv/ccv/test/unit/nnc/backward.tests.c
Line | Count | Source |
1 | | #include "case.h" |
2 | | #include "ccv_case.h" |
3 | | #include "ccv_nnc_case.h" |
4 | | #include <ccv.h> |
5 | | #include <nnc/ccv_nnc.h> |
6 | | #include <nnc/ccv_nnc_easy.h> |
7 | | |
8 | | TEST_SETUP() |
9 | | { |
10 | | ccv_nnc_init(); |
11 | | } |
12 | | |
13 | | TEST_CASE("convolutional network of 3x5 on 21x31 for error backward propagation") |
14 | 1 | { |
15 | 1 | ccv_nnc_tensor_t* a = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 31, 21, 3), 0); |
16 | 1 | ccv_nnc_tensor_t* b = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 31, 21, 32), 0); |
17 | 1 | ccv_nnc_cmd_t forw_cmd = CMD_CONVOLUTION_FORWARD(1, 32, 5, 3, 3); |
18 | 1 | ccv_nnc_hint_t hint = ccv_nnc_hint_auto(forw_cmd.info, a->info, b->info); |
19 | 1 | ccv_nnc_tensor_t* w = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 32, 5, 3, 3), 0); |
20 | 1 | ccv_nnc_tensor_t* bias = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 32), 0); |
21 | 1 | int i, j, k; |
22 | 1.44k | for (i = 0; i < 3 * 5 * 3 * 32; i++1.44k ) |
23 | 1.44k | w->data.f32[i] = 2; |
24 | 1.95k | for (i = 0; i < 21 * 31 * 3; i++1.95k ) |
25 | 1.95k | a->data.f32[i] = 1; |
26 | 33 | for (i = 0; i < 32; i++32 ) |
27 | 32 | bias->data.f32[i] = 0; |
28 | 1 | ccv_nnc_cmd_exec(forw_cmd, hint, 0, TENSOR_LIST(a, w, bias), TENSOR_LIST(b), 0); |
29 | 1 | ccv_nnc_cmd_t back_cmd = CMD_CONVOLUTION_BACKWARD(1, 32, 5, 3, 3); |
30 | 1 | ccv_nnc_tensor_t* gw = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 32, 5, 3, 3), 0); |
31 | 1 | ccv_nnc_tensor_t* gbias = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 32), 0); |
32 | 1 | ccv_nnc_tensor_t* g = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 31, 21, 32), 0); |
33 | 1 | ccv_nnc_tensor_t* h = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 31, 21, 3), 0); |
34 | 20.8k | for (i = 0; i < 21 * 31 * 32; i++20.8k ) |
35 | 20.8k | g->data.f32[i] = 1; |
36 | 1 | ccv_nnc_cmd_exec(back_cmd, hint, 0, TENSOR_LIST(g, a, w), TENSOR_LIST(h, gw, gbias), 0); |
37 | 1 | ccv_dense_matrix_t* dd = ccv_dense_matrix_new(31, 21, CCV_32F | CCV_C3, 0, 0); |
38 | 32 | for (i = 0; i < 31; i++31 ) |
39 | 682 | for (j = 0; 31 j < 21; j++651 ) |
40 | 651 | dd->data.f32[(i * 21 + j) * 3] = |
41 | 651 | dd->data.f32[(i * 21 + j) * 3 + 1] = |
42 | 651 | dd->data.f32[(i * 21 + j) * 3 + 2] = 32 * 2 * (5 + ccv_min(i - 2, 0) + ccv_min(28 - i, 0)) * (3 + ccv_min(j - 1, 0) + ccv_min(19 - j, 0)); |
43 | 1 | REQUIRE_MATRIX_EQ(h, dd, "propagated error doesn't match the expected value"); |
44 | 1 | ccv_matrix_free(dd); |
45 | 1 | float* dw = (float*)ccmalloc(sizeof(float) * 5 * 3 * 3 * 32); |
46 | 33 | for (k = 0; k < 32; k++32 ) |
47 | 192 | for (i = 0; 32 i < 5; i++160 ) |
48 | 640 | for (j = 0; 160 j < 3; j++480 ) |
49 | 480 | dw[k * 5 * 3 * 3 + (i * 3 + j) * 3] = |
50 | 480 | dw[k * 5 * 3 * 3 + (i * 3 + j) * 3 + 1] = |
51 | 480 | dw[k * 5 * 3 * 3 + (i * 3 + j) * 3 + 2] = (31 - abs(i - 2)) * (21 - abs(j - 1)); |
52 | 1 | REQUIRE_ARRAY_EQ_WITH_TOLERANCE(float, dw, gw->data.f32, 5 * 3 * 3 * 32, 1e-4, "weight gradient doesn't match the expected value"); |
53 | 1 | ccfree(dw); |
54 | 1 | float* dbias = (float*)ccmalloc(sizeof(float) * 32); |
55 | 33 | for (i = 0; i < 32; i++32 ) |
56 | 32 | dbias[i] = 21 * 31; |
57 | 1 | REQUIRE_ARRAY_EQ_WITH_TOLERANCE(float, dbias, gbias->data.f32, 32, 1e-4, "bias gradient doesn't match the expected value"); |
58 | 1 | ccfree(dbias); |
59 | 1 | ccv_nnc_tensor_free(a); |
60 | 1 | ccv_nnc_tensor_free(b); |
61 | 1 | ccv_nnc_tensor_free(g); |
62 | 1 | ccv_nnc_tensor_free(h); |
63 | 1 | ccv_nnc_tensor_free(w); |
64 | 1 | ccv_nnc_tensor_free(bias); |
65 | 1 | ccv_nnc_tensor_free(gw); |
66 | 1 | ccv_nnc_tensor_free(gbias); |
67 | 1 | } |
68 | | |
69 | | TEST_CASE("full connect back propagation") |
70 | 1 | { |
71 | 1 | ccv_nnc_tensor_t* a = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 5), 0); |
72 | 1 | ccv_nnc_tensor_t* b = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 4), 0); |
73 | 1 | ccv_nnc_tensor_t* bias = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 4), 0); |
74 | 1 | bias->data.f32[0] = 1; |
75 | 1 | bias->data.f32[1] = 4; |
76 | 1 | bias->data.f32[2] = 2; |
77 | 1 | bias->data.f32[3] = -1; |
78 | 1 | a->data.f32[0] = 5; |
79 | 1 | a->data.f32[1] = -3; |
80 | 1 | a->data.f32[2] = 10; |
81 | 1 | a->data.f32[3] = 11; |
82 | 1 | a->data.f32[4] = -1; |
83 | 1 | float m[] = { |
84 | 1 | 0.5, 0.2, -0.3, 2, 4, |
85 | 1 | 1, 8, 2, 8, -1, |
86 | 1 | 0, 10, -1, -2, 3, |
87 | 1 | 4, 7, 8, 10, 0 |
88 | 1 | }; |
89 | 1 | float ho[] = { |
90 | 1 | 0.5 + 4 - 4, |
91 | 1 | 0.2 + 4 * 8 + 2 * 10 - 7, |
92 | 1 | -0.3 + 4 * 2 - 2 - 8, |
93 | 1 | 2 + 4 * 8 - 2 * 2 - 10, |
94 | 1 | 4 - 4 + 2 * 3, |
95 | 1 | }; |
96 | 1 | ccv_nnc_tensor_t* w = ccv_nnc_tensor_new(m, CPU_TENSOR_NHWC(32F, 4, 5), 0); |
97 | 1 | ccv_nnc_cmd_t forw_cmd = CMD_GEMM_FORWARD(NO_TRANSPOSE, TRANSPOSE(0, 1)); |
98 | 1 | ccv_nnc_cmd_exec(forw_cmd, ccv_nnc_no_hint, 0, TENSOR_LIST(a, w, bias), TENSOR_LIST(b), 0); |
99 | 1 | float bo[] = { |
100 | 1 | 0.5 * 5 - 0.2 * 3 - 0.3 * 10 + 2 * 11 - 4 + 1, |
101 | 1 | 1 * 5 - 8 * 3 + 2 * 10 + 8 * 11 + 1 + 4, |
102 | 1 | -10 * 3 - 10 - 2 * 11 - 3 + 2, |
103 | 1 | 4 * 5 - 7 * 3 + 8 * 10 + 10 * 11 - 1 |
104 | 1 | }; |
105 | 1 | ccv_nnc_tensor_t bot = ccv_nnc_tensor(bo, CPU_TENSOR_NHWC(32F, 4), 0); |
106 | 1 | REQUIRE_TENSOR_EQ(b, &bot, "forward propagation result should match expected value"); |
107 | 1 | ccv_nnc_cmd_t back_cmd = CMD_GEMM_BACKWARD(NO_TRANSPOSE, TRANSPOSE(0, 1)); |
108 | 1 | ccv_nnc_tensor_t* gw = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 4, 5), 0); |
109 | 1 | ccv_nnc_tensor_t* gbias = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 4), 0); |
110 | 1 | ccv_nnc_tensor_t* h = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 5), 0); |
111 | | // Pass in bias as gradient |
112 | 1 | ccv_nnc_cmd_exec(back_cmd, ccv_nnc_no_hint, 0, TENSOR_LIST(bias, a, w), TENSOR_LIST(h, gw, gbias), 0); |
113 | | // Therefore, gradient bias should match bias. |
114 | 1 | REQUIRE_TENSOR_EQ(gbias, bias, "bias gradients should match expected value"); |
115 | 1 | float go[] = { |
116 | 1 | 5, -3, 10, 11, -1, |
117 | 1 | 4 * 5, -4 * 3, 4 * 10, 4 * 11, -4, |
118 | 1 | 2 * 5, -2 * 3, 2 * 10, 2 * 11, -2, |
119 | 1 | -5, 3, -10, -11, 1 |
120 | 1 | }; |
121 | 1 | ccv_nnc_tensor_t got = ccv_nnc_tensor(go, CPU_TENSOR_NHWC(32F, 4, 5), 0); |
122 | 1 | REQUIRE_TENSOR_EQ(gw, &got, "weight gradients should match expected value"); |
123 | 1 | ccv_nnc_tensor_t hot = ccv_nnc_tensor(ho, CPU_TENSOR_NHWC(32F, 5), 0); |
124 | 1 | REQUIRE_TENSOR_EQ(h, &hot, "back propagation error should match expected value"); |
125 | 1 | ccv_nnc_tensor_free(a); |
126 | 1 | ccv_nnc_tensor_free(b); |
127 | 1 | ccv_nnc_tensor_free(w); |
128 | 1 | ccv_nnc_tensor_free(bias); |
129 | 1 | ccv_nnc_tensor_free(h); |
130 | 1 | ccv_nnc_tensor_free(gw); |
131 | 1 | ccv_nnc_tensor_free(gbias); |
132 | 1 | } |
133 | | |
134 | | TEST_CASE("full connect back propagation with batch = 2") |
135 | 1 | { |
136 | 1 | ccv_nnc_tensor_t* a = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 2, 5), 0); |
137 | 1 | ccv_nnc_tensor_t* b = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 2, 4), 0); |
138 | 1 | ccv_nnc_tensor_t* bias = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 4), 0); |
139 | 1 | bias->data.f32[0] = 1; |
140 | 1 | bias->data.f32[1] = 4; |
141 | 1 | bias->data.f32[2] = 2; |
142 | 1 | bias->data.f32[3] = -1; |
143 | 1 | a->data.f32[0] = 5; |
144 | 1 | a->data.f32[1] = -3; |
145 | 1 | a->data.f32[2] = 10; |
146 | 1 | a->data.f32[3] = 11; |
147 | 1 | a->data.f32[4] = -1; |
148 | 1 | a->data.f32[0 + 5] = -5; |
149 | 1 | a->data.f32[1 + 5] = 3; |
150 | 1 | a->data.f32[2 + 5] = -10; |
151 | 1 | a->data.f32[3 + 5] = -11; |
152 | 1 | a->data.f32[4 + 5] = 1; |
153 | 1 | float m[] = { |
154 | 1 | 0.5, 0.2, -0.3, 2, 4, |
155 | 1 | 1, 8, 2, 8, -1, |
156 | 1 | 0, 10, -1, -2, 3, |
157 | 1 | 4, 7, 8, 10, 0 |
158 | 1 | }; |
159 | 1 | float ho[] = { |
160 | 1 | -(0.5 + 4 - 4), |
161 | 1 | -(0.2 + 4 * 8 + 2 * 10 - 7), |
162 | 1 | -(-0.3 + 4 * 2 - 2 - 8), |
163 | 1 | -(2 + 4 * 8 - 2 * 2 - 10), |
164 | 1 | -(4 - 4 + 2 * 3), |
165 | 1 | 0.5 + 4 - 4, |
166 | 1 | 0.2 + 4 * 8 + 2 * 10 - 7, |
167 | 1 | -0.3 + 4 * 2 - 2 - 8, |
168 | 1 | 2 + 4 * 8 - 2 * 2 - 10, |
169 | 1 | 4 - 4 + 2 * 3, |
170 | 1 | }; |
171 | 1 | ccv_nnc_tensor_t* w = ccv_nnc_tensor_new(m, CPU_TENSOR_NHWC(32F, 4, 5), 0); |
172 | 1 | ccv_nnc_cmd_t forw_cmd = CMD_GEMM_FORWARD(NO_TRANSPOSE, TRANSPOSE(0, 1)); |
173 | 1 | ccv_nnc_cmd_exec(forw_cmd, ccv_nnc_no_hint, 0, TENSOR_LIST(a, w, bias), TENSOR_LIST(b), 0); |
174 | 1 | float bo[] = { |
175 | 1 | 0.5 * 5 - 0.2 * 3 - 0.3 * 10 + 2 * 11 - 4 + 1, |
176 | 1 | 1 * 5 - 8 * 3 + 2 * 10 + 8 * 11 + 1 + 4, |
177 | 1 | -10 * 3 - 10 - 2 * 11 - 3 + 2, |
178 | 1 | 4 * 5 - 7 * 3 + 8 * 10 + 10 * 11 - 1, |
179 | 1 | -(0.5 * 5 - 0.2 * 3 - 0.3 * 10 + 2 * 11 - 4) + 1, |
180 | 1 | -(1 * 5 - 8 * 3 + 2 * 10 + 8 * 11 + 1) + 4, |
181 | 1 | -(-10 * 3 - 10 - 2 * 11 - 3) + 2, |
182 | 1 | -(4 * 5 - 7 * 3 + 8 * 10 + 10 * 11) - 1 |
183 | 1 | }; |
184 | 1 | ccv_nnc_tensor_t bot = ccv_nnc_tensor(bo, CPU_TENSOR_NHWC(32F, 2, 4), 0); |
185 | 1 | REQUIRE_TENSOR_EQ(b, &bot, "forward propagation result should match expected value"); |
186 | 1 | ccv_nnc_cmd_t back_cmd = CMD_GEMM_BACKWARD(NO_TRANSPOSE, TRANSPOSE(0, 1)); |
187 | 1 | ccv_nnc_tensor_t* gw = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 4, 5), 0); |
188 | 1 | ccv_nnc_tensor_t* gbias = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 4), 0); |
189 | 1 | ccv_nnc_tensor_t* h = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 2, 5), 0); |
190 | 1 | ccv_nnc_tensor_t* g = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 2, 4), 0); |
191 | 1 | int i; |
192 | 5 | for (i = 0; i < 4; i++4 ) |
193 | 4 | { |
194 | 4 | g->data.f32[i] = -bias->data.f32[i]; |
195 | 4 | g->data.f32[i + 4] = bias->data.f32[i]; |
196 | 4 | } |
197 | | // Pass in bias as gradient |
198 | 1 | ccv_nnc_cmd_exec(back_cmd, ccv_nnc_no_hint, 0, TENSOR_LIST(g, a, w), TENSOR_LIST(h, gw, gbias), 0); |
199 | | // Therefore, gradient bias should match bias. |
200 | 5 | for (i = 0; i < 4; i++4 ) |
201 | 4 | bias->data.f32[i] = 0; |
202 | 1 | REQUIRE_TENSOR_EQ(gbias, bias, "bias gradients should match expected value"); |
203 | 1 | float go[] = { |
204 | 1 | 5, -3, 10, 11, -1, |
205 | 1 | 4 * 5, -4 * 3, 4 * 10, 4 * 11, -4, |
206 | 1 | 2 * 5, -2 * 3, 2 * 10, 2 * 11, -2, |
207 | 1 | -5, 3, -10, -11, 1 |
208 | 1 | }; |
209 | 21 | for (i = 0; i < 5 * 4; i++20 ) |
210 | 20 | go[i] = -go[i] * 2; // Because the gradient is negative in the first example, and the input is negative in the second example, we basically doubled the weight gradients. |
211 | 1 | ccv_nnc_tensor_t got = ccv_nnc_tensor(go, CPU_TENSOR_NHWC(32F, 4, 5), 0); |
212 | 1 | REQUIRE_TENSOR_EQ(gw, &got, "weight gradients should match expected value"); |
213 | 1 | ccv_nnc_tensor_t hot = ccv_nnc_tensor(ho, CPU_TENSOR_NHWC(32F, 2, 5), 0); |
214 | 1 | REQUIRE_TENSOR_EQ(h, &hot, "back propagation error should match expected value"); |
215 | 1 | ccv_nnc_tensor_free(a); |
216 | 1 | ccv_nnc_tensor_free(b); |
217 | 1 | ccv_nnc_tensor_free(w); |
218 | 1 | ccv_nnc_tensor_free(bias); |
219 | 1 | ccv_nnc_tensor_free(g); |
220 | 1 | ccv_nnc_tensor_free(h); |
221 | 1 | ccv_nnc_tensor_free(gw); |
222 | 1 | ccv_nnc_tensor_free(gbias); |
223 | 1 | } |
224 | | |
225 | | #include "case_main.h" |