/home/liu/actions-runner/_work/ccv/ccv/test/unit/nnc/while.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 | | #include <nnc/_ccv_nnc_graph.h> |
8 | | #include <3rdparty/dsfmt/dSFMT.h> |
9 | | |
10 | | TEST_SETUP() |
11 | | { |
12 | | ccv_nnc_init(); |
13 | | } |
14 | | |
15 | | static int while_4(ccv_nnc_tensor_t* const* const inputs, const int input_size, const void* const data) |
16 | 15 | { |
17 | 15 | return inputs[0]->data.i64[0] < 4; |
18 | 15 | } |
19 | | |
20 | | TEST_CASE("graph with a while loop to compute back propagation 0.34 * 1.11 ^ 5") |
21 | 1 | { |
22 | 1 | ccv_nnc_graph_t* graph = ccv_nnc_graph_new(); |
23 | 1 | ccv_nnc_tensor_t* y = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 1), 0); |
24 | 1 | ccv_nnc_tensor_t* x0 = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 1), 0); |
25 | 1 | ccv_nnc_tensor_t* x = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 1), 0); |
26 | 1 | x->type |= CCV_TAPE_ALLOC; |
27 | 1 | ccv_nnc_tensor_t* z = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 1), 0); |
28 | 1 | z->type |= CCV_TAPE_ALLOC; |
29 | 1 | ccv_nnc_tensor_t* g = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 1), 0); |
30 | 1 | ccv_nnc_graph_t* while_graph = ccv_nnc_graph_new(); |
31 | 1 | ccv_nnc_graph_exec_t loop = ccv_nnc_graph_while(graph, CCV_NNC_GRAPH_FORWARD, while_graph); |
32 | 1 | ccv_nnc_tensor_multiview_t xx; |
33 | 1 | ccv_nnc_tensor_multiview((ccv_nnc_tensor_t*[]){ |
34 | 1 | x0, z, x |
35 | 1 | }, CCV_NNC_MULTIVIEW_K1N, 2, while_graph, &xx); |
36 | 1 | xx.type |= CCV_TAPE_ALLOC; |
37 | 1 | ccv_nnc_tensor_multiview_t zz; |
38 | 1 | ccv_nnc_tensor_multiview((ccv_nnc_tensor_t*[]){ |
39 | 1 | z, x |
40 | 1 | }, CCV_NNC_MULTIVIEW_K0N, 2, while_graph, &zz); |
41 | 1 | zz.type |= CCV_TAPE_ALLOC; |
42 | 1 | ccv_nnc_graph_exec_t prod0 = ccv_nnc_graph_exec_new(while_graph, CMD_EWPROD_FORWARD(), ccv_nnc_no_hint, TENSOR_LIST(y, (ccv_nnc_tensor_t*)&xx), TENSOR_LIST((ccv_nnc_tensor_t*)&zz)); |
43 | 1 | ccv_nnc_graph_exec_t noop = ccv_nnc_graph_exec_new(while_graph, CMD_NOOP(), ccv_nnc_no_hint, 0, 0, 0, 0); |
44 | 1 | ccv_nnc_graph_exec_concat(while_graph, prod0, noop); |
45 | 1 | ccv_nnc_graph_set_sources(while_graph, GRAPH_EXEC_LIST(prod0)); |
46 | 1 | ccv_nnc_graph_set_destinations(while_graph, GRAPH_EXEC_LIST(noop)); |
47 | 1 | ccv_nnc_tensor_t count_tensor = ccv_nnc_tensor_for_while_count(while_graph); |
48 | 1 | ccv_nnc_graph_set_while_expr(while_graph, while_4, 0, TENSOR_LIST(&count_tensor), GRAPH_EXEC_LIST(noop)); |
49 | 1 | ccv_nnc_graph_t* while_back_graph = ccv_nnc_graph_new(); |
50 | 1 | while_back_graph->pair = while_graph; |
51 | 1 | ccv_nnc_graph_exec_t back_loop = ccv_nnc_graph_while(graph, CCV_NNC_GRAPH_BACKWARD, while_back_graph); |
52 | 1 | ccv_nnc_tensor_t* dx = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 1), 0); |
53 | 1 | ccv_nnc_graph_exec_t back_prod0 = ccv_nnc_graph_exec_new(while_back_graph, CMD_EWPROD_BACKWARD(), ccv_nnc_no_hint, TENSOR_LIST(g, y, (ccv_nnc_tensor_t*)&xx, (ccv_nnc_tensor_t*)&zz), TENSOR_LIST(dx, g)); |
54 | 1 | ccv_nnc_graph_exec_t back_noop = ccv_nnc_graph_exec_new(while_back_graph, CMD_NOOP(), ccv_nnc_no_hint, 0, 0, 0, 0); |
55 | 1 | ccv_nnc_graph_exec_concat(while_back_graph, back_noop, back_prod0); |
56 | 1 | ccv_nnc_graph_set_sources(while_back_graph, GRAPH_EXEC_LIST(back_noop)); |
57 | 1 | ccv_nnc_graph_set_destinations(while_back_graph, GRAPH_EXEC_LIST(back_prod0)); |
58 | 1 | ccv_nnc_graph_set_while_expr(while_back_graph, while_4, 0, 0, 0, GRAPH_EXEC_LIST(back_noop)); |
59 | 1 | ccv_nnc_graph_exec_concat(graph, loop, back_loop); |
60 | 1 | GRAPH_GEN(graph, CCV_NNC_LONG_DOT_GRAPH); |
61 | 1 | x0->data.f32[0] = 0.34; |
62 | 1 | y->data.f32[0] = 1.11; |
63 | 1 | g->data.f32[0] = 1; |
64 | 1 | ccv_nnc_tensor_tape_t* tape = ccv_nnc_tensor_tape_new(); |
65 | 1 | ccv_nnc_graph_run(graph, 0, GRAPH_EXEC_LIST(loop), GRAPH_EXEC_LIST(back_loop), tape, 0); |
66 | 1 | ccv_nnc_graph_free(graph); |
67 | 1 | REQUIRE_EQ_WITH_TOLERANCE(g->data.f32[0], 1.11 * 1.11 * 1.11 * 1.11 * 1.11, 1e-6, "back propagation of 0.34 * 1.11 ^ 5 should be 1.11 ^ 5"); |
68 | 1 | ccv_nnc_tensor_tape_free(tape); |
69 | 1 | ccv_nnc_tensor_multiview_free(xx); |
70 | 1 | ccv_nnc_tensor_multiview_free(zz); |
71 | 1 | ccv_nnc_tensor_free(x0); |
72 | 1 | ccv_nnc_tensor_free(dx); |
73 | 1 | ccv_nnc_tensor_free(x); |
74 | 1 | ccv_nnc_tensor_free(y); |
75 | 1 | ccv_nnc_tensor_free(z); |
76 | 1 | ccv_nnc_tensor_free(g); |
77 | 1 | } |
78 | | |
79 | | TEST_CASE("symbolic graph with a while loop z = log(x * y) (x <- z) 5 times, then u = v * z, compute y'") |
80 | 1 | { |
81 | 1 | ccv_nnc_symbolic_graph_t* symbolic_graph = ccv_nnc_symbolic_graph_new(); |
82 | 1 | ccv_nnc_tensor_symbol_t x = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1), "x"); |
83 | 1 | ccv_nnc_tensor_symbol_t y = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1), "y"); |
84 | 1 | ccv_nnc_symbolic_graph_t* while_graph = ccv_nnc_symbolic_graph_new(); |
85 | 1 | ccv_nnc_tensor_symbol_t xy = ccv_nnc_tensor_symbol_new(while_graph, CPU_TENSOR_NHWC(32F, 1), "xy"); |
86 | 1 | ccv_nnc_tensor_symbol_t z = ccv_nnc_tensor_symbol_new(while_graph, CPU_TENSOR_NHWC(32F, 1), "z"); |
87 | 1 | ccv_nnc_tensor_symbol_t u = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1), "u"); |
88 | 1 | ccv_nnc_tensor_symbol_t v = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1), "v"); |
89 | 1 | ccv_nnc_symbolic_graph_while(symbolic_graph, CCV_NNC_GRAPH_FORWARD, while_graph, "while0"); |
90 | 1 | ccv_nnc_graph_exec_symbol_t prod0 = ccv_nnc_graph_exec_symbol_new(while_graph, CMD_EWPROD_FORWARD(), TENSOR_SYMBOL_LIST(x, y), TENSOR_SYMBOL_LIST(xy), "prod0"); |
91 | 1 | ccv_nnc_graph_exec_symbol_t log0 = ccv_nnc_graph_exec_symbol_new(while_graph, CMD_EWLOG_FORWARD(), TENSOR_SYMBOL_LIST(xy), TENSOR_SYMBOL_LIST(z), "log0"); |
92 | 1 | ccv_nnc_graph_exec_symbol_autogen(while_graph, GRAPH_EXEC_SYMBOL_LIST(prod0, log0), 0); |
93 | 1 | ccv_nnc_graph_exec_symbol_t noop = ccv_nnc_graph_exec_symbol_new(while_graph, CMD_NOOP(), 0, 0, 0, 0, "noop"); |
94 | 1 | ccv_nnc_graph_exec_symbol_concat(while_graph, log0, noop); |
95 | 1 | ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_EWPROD_FORWARD(), TENSOR_SYMBOL_LIST(z, v), TENSOR_SYMBOL_LIST(u), "prod1"); |
96 | 1 | ccv_nnc_graph_exec_symbol_autogen(symbolic_graph, 0, 0, CCV_NNC_AUTOGEN_ALL_EXECS | CCV_NNC_AUTOGEN_SOURCES_AND_DESTINATIONS); |
97 | 1 | ccv_nnc_symbolic_graph_set_while_expr(while_graph, while_4, 0, TENSOR_SYMBOL_LIST(ccv_nnc_tensor_symbol_for_while_count(while_graph)), GRAPH_EXEC_SYMBOL_LIST(noop)); |
98 | 1 | ccv_nnc_symbolic_graph_set_carry_overs(while_graph, TENSOR_SYMBOL_MAP(KV(z, x))); |
99 | 1 | ccv_nnc_symbolic_graph_set_sources(while_graph, GRAPH_EXEC_SYMBOL_LIST(prod0)); |
100 | 1 | ccv_nnc_symbolic_graph_set_destinations(while_graph, GRAPH_EXEC_SYMBOL_LIST(noop)); |
101 | 1 | ccv_nnc_graph_t* graph = 0; |
102 | 1 | ccv_nnc_tensor_arena_t* tensor_arena = 0; |
103 | 1 | ccv_nnc_graph_exec_arena_t* graph_exec_arena = 0; |
104 | 1 | ccv_nnc_symbolic_graph_compile(symbolic_graph, ccv_nnc_default_compile_params, 0, 0, 0, 0, ccv_nnc_symbolic_graph_sources(symbolic_graph), ccv_nnc_symbolic_graph_source_size(symbolic_graph), ccv_nnc_symbolic_graph_destinations(symbolic_graph), ccv_nnc_symbolic_graph_destination_size(symbolic_graph), &graph, &tensor_arena, &graph_exec_arena); |
105 | 1 | ccv_nnc_tensor_t* x_tensor = ccv_nnc_tensor_from_symbol(tensor_arena, x); |
106 | 1 | ccv_nnc_tensor_t* y_tensor = ccv_nnc_tensor_from_symbol(tensor_arena, y); |
107 | 1 | ccv_nnc_tensor_t* v_tensor = ccv_nnc_tensor_from_symbol(tensor_arena, v); |
108 | 1 | x_tensor->data.f32[0] = 1; |
109 | 1 | y_tensor->data.f32[0] = 3.2; |
110 | 1 | v_tensor->data.f32[0] = 0.22; |
111 | 1 | ccv_nnc_graph_run(graph, 0, TRAVERSE_FULL, 0, 0); |
112 | 1 | ccv_nnc_tensor_t* u_tensor = ccv_nnc_tensor_from_symbol(tensor_arena, u); |
113 | 1 | float z0 = 1, y0 = 3.2; |
114 | 1 | int i; |
115 | 6 | for (i = 0; i < 5; i++5 ) |
116 | 5 | z0 = log(z0 * y0); |
117 | 1 | z0 = 0.22 * z0; |
118 | 1 | REQUIRE_EQ_WITH_TOLERANCE(u_tensor->data.f32[0], z0, 1e-6, "u should match the for loop result"); |
119 | 1 | ccv_nnc_graph_exec_arena_free(graph_exec_arena); |
120 | 1 | ccv_nnc_tensor_arena_free(tensor_arena); |
121 | 1 | ccv_nnc_graph_free(graph); |
122 | 1 | ccv_nnc_symbolic_graph_backward(symbolic_graph, TENSOR_SYMBOL_LIST(u), TENSOR_SYMBOL_LIST(y), ccv_nnc_symbolic_graph_sources(symbolic_graph), ccv_nnc_symbolic_graph_source_size(symbolic_graph), ccv_nnc_symbolic_graph_destinations(symbolic_graph), ccv_nnc_symbolic_graph_destination_size(symbolic_graph)); |
123 | 1 | SYMBOLIC_GRAPH_GEN(symbolic_graph, CCV_NNC_LONG_DOT_GRAPH); |
124 | 1 | ccv_nnc_tensor_symbol_t dy = ccv_nnc_tensor_symbol_for_backward(symbolic_graph, y); |
125 | 1 | ccv_nnc_graph_exec_symbol_t dyx = ccv_nnc_graph_exec_symbol_for_backward(symbolic_graph, dy); |
126 | 1 | ccv_nnc_symbolic_graph_compile(symbolic_graph, ccv_nnc_default_compile_params, 0, 0, 0, 0, ccv_nnc_symbolic_graph_sources(symbolic_graph), ccv_nnc_symbolic_graph_source_size(symbolic_graph), &dyx, 1, &graph, &tensor_arena, &graph_exec_arena); |
127 | 1 | GRAPH_GEN(graph, CCV_NNC_LONG_DOT_GRAPH); |
128 | 1 | x_tensor = ccv_nnc_tensor_from_symbol(tensor_arena, x); |
129 | 1 | y_tensor = ccv_nnc_tensor_from_symbol(tensor_arena, y); |
130 | 1 | v_tensor = ccv_nnc_tensor_from_symbol(tensor_arena, v); |
131 | 1 | x_tensor->data.f32[0] = 1; |
132 | 1 | y_tensor->data.f32[0] = 3.2; |
133 | 1 | v_tensor->data.f32[0] = 0.22; |
134 | 1 | ccv_nnc_tensor_t* du_tensor = ccv_nnc_tensor_from_symbol(tensor_arena, ccv_nnc_tensor_symbol_for_backward(symbolic_graph, u)); |
135 | 1 | du_tensor->data.f32[0] = 1; |
136 | 1 | ccv_nnc_tensor_tape_t* tape = ccv_nnc_tensor_tape_new(); |
137 | 1 | ccv_nnc_graph_run(graph, 0, TRAVERSE_FULL, tape, 0); |
138 | 1 | ccv_nnc_tensor_t* dy_tensor = ccv_nnc_tensor_from_symbol(tensor_arena, dy); |
139 | | // Effectively, we are computing: |
140 | | // D[Log[Log[Log[Log[Log[x * y] * y] * y] * y] * y] * v, y] |
141 | | // From WolframAlpha: http://www.wolframalpha.com/input/?i=D%5BLog%5BLog%5BLog%5BLog%5BLog%5Bx+*+y%5D+*+y%5D+*+y%5D+*+y%5D+*+y%5D+*+v,+y%5D |
142 | 1 | const float dya = 0.22 * (((1 / log(1 * 3.2) + 1) / (log(3.2 * log(1 * 3.2)) * log(3.2 * log(3.2 * log(1 * 3.2)))) + 1 / log(3.2 * log(3.2 * log(1 * 3.2))) + 1) / (3.2 * log(3.2 * log(3.2 * log(3.2 * log(1 * 3.2))))) + 1 / 3.2); |
143 | 1 | REQUIRE_EQ_WITH_TOLERANCE(dy_tensor->data.f32[0], dya, 1e-6, "back propagation of this while loop should match WolframAlpha result"); |
144 | 1 | ccv_nnc_tensor_tape_free(tape); |
145 | 1 | ccv_nnc_symbolic_graph_free(symbolic_graph); |
146 | 1 | ccv_nnc_graph_exec_arena_free(graph_exec_arena); |
147 | 1 | ccv_nnc_tensor_arena_free(tensor_arena); |
148 | 1 | ccv_nnc_graph_free(graph); |
149 | 1 | } |
150 | | |
151 | | #include "case_main.h" |