/home/liu/actions-runner/_work/ccv/ccv/test/unit/nnc/gradient.tests.c
Line | Count | Source |
1 | | #include "case.h" |
2 | | #include "ccv_case.h" |
3 | | #include <ccv.h> |
4 | | #include <nnc/ccv_nnc.h> |
5 | | #include <nnc/ccv_nnc_easy.h> |
6 | | #include "3rdparty/dsfmt/dSFMT.h" |
7 | | |
8 | | TEST_SETUP() |
9 | 94 | { |
10 | 94 | ccv_nnc_init(); |
11 | 94 | } |
12 | | |
13 | | // five-stencil constants |
14 | | static double fs[4] = { 1, -8, 8, -1 }; |
15 | | static double fsh[4] = { -2, -1, 1, 2 }; |
16 | | |
17 | | TEST_CASE("numerical gradient versus analytical gradient for convolutional network") |
18 | 1 | { |
19 | 1 | ccv_nnc_tensor_t* a = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 31, 21, 2), 0); |
20 | 1 | ccv_nnc_tensor_t* b = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 31, 21, 4), 0); |
21 | 1 | ccv_nnc_cmd_t forw_cmd = CMD_CONVOLUTION_FORWARD(1, 4, 5, 3, 2); |
22 | 1 | ccv_nnc_hint_t hint = ccv_nnc_hint_auto(forw_cmd.info, a->info, b->info); |
23 | 1 | ccv_nnc_tensor_t* w = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 4, 5, 3, 2), 0); |
24 | 1 | ccv_nnc_tensor_t* bias = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 4), 0); |
25 | 1 | dsfmt_t dsfmt; |
26 | 1 | dsfmt_init_gen_rand(&dsfmt, 1); |
27 | 1 | int i, j; |
28 | 121 | for (i = 0; i < 2 * 3 * 5 * 4; i++120 ) |
29 | 120 | w->data.f32[i] = (dsfmt_genrand_open_close(&dsfmt) * 2 - 1) * 1.41421356237 / sqrtf(21 * 31 * 2 + 21 * 31 * 4); |
30 | 1 | float denom = (21 * 31 * 2 - 1) * 21 * 31 * 2; |
31 | 1.30k | for (i = 0; i < 21 * 31 * 2; i++1.30k ) |
32 | 1.30k | a->data.f32[i] = (float)(i - 21 * 31) / denom; |
33 | 5 | for (i = 0; i < 4; i++4 ) |
34 | 4 | bias->data.f32[i] = 0; |
35 | 1 | ccv_nnc_cmd_exec(forw_cmd, hint, 0, TENSOR_LIST(a, w, bias), TENSOR_LIST(b), 0); |
36 | 1 | ccv_nnc_tensor_t* ba = ccv_nnc_tensor_new(b->data.f32, CPU_TENSOR_NHWC(32F, 31 * 21 * 4), 0); |
37 | 1 | ccv_nnc_tensor_t* m = ccv_nnc_tensor_new(0, ba->info, 0); |
38 | 1 | ccv_nnc_cmd_exec(CMD_SOFTMAX_FORWARD(), hint, 0, TENSOR_LIST(ba), TENSOR_LIST(m), 0); |
39 | 1 | ccv_nnc_cmd_t back_cmd = CMD_CONVOLUTION_BACKWARD(1, 4, 5, 3, 2); |
40 | 1 | ccv_nnc_tensor_t* gw = ccv_nnc_tensor_new(0, w->info, 0); |
41 | 1 | ccv_nnc_tensor_t* gbias = ccv_nnc_tensor_new(0, bias->info, 0); |
42 | 1 | ccv_nnc_tensor_t* g = ccv_nnc_tensor_new(0, b->info, 0); |
43 | 1 | ccv_nnc_tensor_t* h = ccv_nnc_tensor_new(0, a->info, 0); |
44 | 2.60k | for (i = 0; i < 21 * 31 * 4; i++2.60k ) |
45 | 2.60k | g->data.f32[i] = m->data.f32[i] - (i == 24); |
46 | 1 | ccv_nnc_cmd_exec(back_cmd, hint, 0, TENSOR_LIST(g, a, w), TENSOR_LIST(h, gw, gbias), 0); |
47 | | // Now doing numeric gradient computation |
48 | 1 | static const double eps = 0.001; |
49 | 1 | float* dw = (float*)ccmalloc(sizeof(float) * 2 * 3 * 5 * 4); |
50 | 121 | for (i = 0; i < 2 * 3 * 5 * 4; i++120 ) |
51 | 120 | { |
52 | 120 | double vw = 0; |
53 | 600 | for (j = 0; j < 4; j++480 ) |
54 | 480 | { |
55 | 480 | float old_w = w->data.f32[i]; |
56 | 480 | w->data.f32[i] += fsh[j] * eps; |
57 | 480 | ccv_nnc_cmd_exec(forw_cmd, hint, 0, TENSOR_LIST(a, w, bias), TENSOR_LIST(b), 0); |
58 | 480 | ccv_nnc_cmd_exec(CMD_SOFTMAX_FORWARD(), hint, 0, TENSOR_LIST(ba), TENSOR_LIST(m), 0); |
59 | 480 | vw += -log(m->data.f32[24]) * fs[j]; |
60 | 480 | w->data.f32[i] = old_w; |
61 | 480 | } |
62 | 120 | dw[i] = vw / (12 * eps); |
63 | 120 | } |
64 | 1 | float* dbias = (float*)ccmalloc(sizeof(float) * 4); |
65 | 5 | for (i = 0; i < 4; i++4 ) |
66 | 4 | { |
67 | 4 | dbias[i] = 0; |
68 | 20 | for (j = 0; j < 4; j++16 ) |
69 | 16 | { |
70 | 16 | float old_bias = bias->data.f32[i]; |
71 | 16 | bias->data.f32[i] += fsh[j] * eps; |
72 | 16 | ccv_nnc_cmd_exec(forw_cmd, hint, 0, TENSOR_LIST(a, w, bias), TENSOR_LIST(b), 0); |
73 | 16 | ccv_nnc_cmd_exec(CMD_SOFTMAX_FORWARD(), hint, 0, TENSOR_LIST(ba), TENSOR_LIST(m), 0); |
74 | 16 | dbias[i] += -logf(m->data.f32[24]) * fs[j]; |
75 | 16 | bias->data.f32[i] = old_bias; |
76 | 16 | } |
77 | 4 | dbias[i] *= 1.0 / (12 * eps); |
78 | 4 | } |
79 | 1 | REQUIRE_ARRAY_EQ_WITHIN_ANGLE_AND_MAGNITUDE(float, dw, gw->data.f32, 2 * 3 * 5 * 4, 30, 2e-1, "weight gradient from analytical method doesn't match the one from numerical method"); |
80 | 1 | REQUIRE_ARRAY_EQ_WITHIN_ANGLE_AND_MAGNITUDE(float, dbias, gbias->data.f32, 4, 30, 2e-1, "bias gradient from analytical method doesn't match the one from numerical method"); |
81 | 1 | ccfree(dw); |
82 | 1 | ccfree(dbias); |
83 | 1 | ccv_nnc_tensor_free(a); |
84 | 1 | ccv_nnc_tensor_free(b); |
85 | 1 | ccv_nnc_tensor_free(ba); |
86 | 1 | ccv_nnc_tensor_free(m); |
87 | 1 | ccv_nnc_tensor_free(g); |
88 | 1 | ccv_nnc_tensor_free(h); |
89 | 1 | ccv_nnc_tensor_free(w); |
90 | 1 | ccv_nnc_tensor_free(bias); |
91 | 1 | ccv_nnc_tensor_free(gw); |
92 | 1 | ccv_nnc_tensor_free(gbias); |
93 | 1 | } |
94 | | |
95 | | #include "case_main.h" |