1 /* Copyright 2015 The TensorFlow Authors. All Rights Reserved.
2
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15
16 #include "tensorflow/core/kernels/eigen_backward_cuboid_convolutions.h"
17
18 #include "tensorflow/core/platform/test.h"
19
20 namespace Eigen {
21
22 namespace {
EigenApprox(float a,float b)23 void EigenApprox(float a, float b) {
24 ASSERT_TRUE(std::abs(a - b) <= std::min(std::abs(a), std::abs(b)) * 1e-3);
25 }
ceil_div(int a,int b)26 static int ceil_div(int a, int b) { return (a + b - 1) / b; }
27 } // namespace
28
TEST(EigenBackwardSpatialConvolutionsTest,test_simple_cuboid_convolution_backward_input_valid)29 TEST(EigenBackwardSpatialConvolutionsTest,
30 test_simple_cuboid_convolution_backward_input_valid) {
31 const int input_depth = 2;
32 const int input_planes = 5;
33 const int input_rows = 3;
34 const int input_cols = 4;
35 const int patch_rows = 2;
36 const int patch_cols = 2;
37 const int patch_planes = 2;
38 const int output_rows = input_rows - patch_rows + 1;
39 const int output_cols = input_cols - patch_cols + 1;
40 const int output_planes = input_planes - patch_planes + 1;
41 const int output_depth = 5;
42
43 Tensor<float, 4> input_backward(input_depth, input_planes, input_rows,
44 input_cols);
45 Tensor<float, 5> kernel(output_depth, input_depth, patch_planes, patch_rows,
46 patch_cols);
47 Tensor<float, 4> output_backward(output_depth, output_planes, output_rows,
48 output_cols);
49
50 output_backward = output_backward.constant(11.0f) + output_backward.random();
51 kernel = kernel.constant(2.0f) + kernel.random();
52 input_backward.setRandom();
53
54 input_backward = CuboidConvolutionBackwardInput(
55 kernel, output_backward, input_planes, input_rows, input_cols);
56
57 EXPECT_EQ(input_backward.dimension(3), input_cols);
58 EXPECT_EQ(input_backward.dimension(2), input_rows);
59 EXPECT_EQ(input_backward.dimension(1), input_planes);
60 EXPECT_EQ(input_backward.dimension(0), input_depth);
61
62 for (int id = 0; id < input_depth; ++id) {
63 for (int i = 0; i < input_planes; ++i) {
64 for (int j = 0; j < input_rows; ++j) {
65 for (int k = 0; k < input_cols; ++k) {
66 float expected = 0.0f;
67 for (int c = 0; c < patch_cols; ++c) {
68 for (int r = 0; r < patch_rows; ++r) {
69 for (int p = 0; p < patch_planes; ++p) {
70 for (int od = 0; od < output_depth; ++od) {
71 int output_j = j - r;
72 int output_k = k - c;
73 int output_i = i - p;
74 if (output_i >= 0 && output_i < output_planes &&
75 output_j >= 0 && output_j < output_rows &&
76 output_k >= 0 && output_k < output_cols) {
77 expected +=
78 output_backward(od, output_i, output_j, output_k) *
79 kernel(od, id, p, r, c);
80 }
81 }
82 }
83 }
84 }
85 EigenApprox(input_backward(id, i, j, k), expected);
86 }
87 }
88 }
89 }
90 }
91
TEST(EigenBackwardSpatialConvolutionsTest,test_simple_cuboid_convolution_backward_input_valid_row_major)92 TEST(EigenBackwardSpatialConvolutionsTest,
93 test_simple_cuboid_convolution_backward_input_valid_row_major) {
94 const int input_depth = 2;
95 const int input_planes = 5;
96 const int input_rows = 3;
97 const int input_cols = 4;
98 const int patch_rows = 2;
99 const int patch_cols = 2;
100 const int patch_planes = 2;
101 const int output_rows = input_rows - patch_rows + 1;
102 const int output_cols = input_cols - patch_cols + 1;
103 const int output_planes = input_planes - patch_planes + 1;
104 const int output_depth = 5;
105
106 Tensor<float, 4, RowMajor> input_backward(input_cols, input_rows,
107 input_planes, input_depth);
108 Tensor<float, 5, RowMajor> kernel(patch_cols, patch_rows, patch_planes,
109 input_depth, output_depth);
110 Tensor<float, 4, RowMajor> output_backward(output_cols, output_rows,
111 output_planes, output_depth);
112
113 output_backward = output_backward.constant(11.0f) + output_backward.random();
114 kernel = kernel.constant(2.0f) + kernel.random();
115 input_backward.setRandom();
116
117 input_backward = CuboidConvolutionBackwardInput(
118 kernel, output_backward, input_planes, input_rows, input_cols);
119
120 EXPECT_EQ(input_backward.dimension(0), input_cols);
121 EXPECT_EQ(input_backward.dimension(1), input_rows);
122 EXPECT_EQ(input_backward.dimension(2), input_planes);
123 EXPECT_EQ(input_backward.dimension(3), input_depth);
124
125 for (int id = 0; id < input_depth; ++id) {
126 for (int i = 0; i < input_planes; ++i) {
127 for (int j = 0; j < input_rows; ++j) {
128 for (int k = 0; k < input_cols; ++k) {
129 float expected = 0.0f;
130 for (int c = 0; c < patch_cols; ++c) {
131 for (int r = 0; r < patch_rows; ++r) {
132 for (int p = 0; p < patch_planes; ++p) {
133 for (int od = 0; od < output_depth; ++od) {
134 int output_j = j - r;
135 int output_k = k - c;
136 int output_i = i - p;
137 if (output_i >= 0 && output_i < output_planes &&
138 output_j >= 0 && output_j < output_rows &&
139 output_k >= 0 && output_k < output_cols) {
140 expected +=
141 output_backward(output_k, output_j, output_i, od) *
142 kernel(c, r, p, id, od);
143 }
144 }
145 }
146 }
147 }
148 EigenApprox(input_backward(k, j, i, id), expected);
149 }
150 }
151 }
152 }
153 }
154
TEST(EigenBackwardSpatialConvolutionsTest,test_simple_cuboid_convolution_backward_input_same)155 TEST(EigenBackwardSpatialConvolutionsTest,
156 test_simple_cuboid_convolution_backward_input_same) {
157 const int input_depth = 2;
158 const int input_planes = 5;
159 const int input_rows = 3;
160 const int input_cols = 4;
161 const int patch_rows = 3;
162 const int patch_cols = 2;
163 const int patch_planes = 4;
164 const int output_rows = input_rows;
165 const int output_cols = input_cols;
166 const int output_planes = input_planes;
167 const int output_depth = 5;
168
169 Tensor<float, 4> input_backward(input_depth, input_planes, input_rows,
170 input_cols);
171 Tensor<float, 5> kernel(output_depth, input_depth, patch_planes, patch_rows,
172 patch_cols);
173 Tensor<float, 4> output_backward(output_depth, output_planes, output_rows,
174 output_cols);
175
176 output_backward = output_backward.constant(11.0f) + output_backward.random();
177 kernel = kernel.constant(2.0f) + kernel.random();
178 input_backward.setRandom();
179
180 input_backward = CuboidConvolutionBackwardInput(
181 kernel, output_backward, input_planes, input_rows, input_cols);
182
183 EXPECT_EQ(input_backward.dimension(3), input_cols);
184 EXPECT_EQ(input_backward.dimension(2), input_rows);
185 EXPECT_EQ(input_backward.dimension(1), input_planes);
186 EXPECT_EQ(input_backward.dimension(0), input_depth);
187
188 const int dz = patch_planes - 1;
189 const int dy = patch_rows - 1;
190 const int dx = patch_cols - 1;
191
192 const int forward_pad_x = dx / 2;
193 const int forward_pad_y = dy / 2;
194 const int forward_pad_z = dz / 2;
195
196 for (int id = 0; id < input_depth; ++id) {
197 for (int i = 0; i < input_planes; ++i) {
198 for (int j = 0; j < input_rows; ++j) {
199 for (int k = 0; k < input_cols; ++k) {
200 float expected = 0.0f;
201 for (int c = 0; c < patch_cols; ++c) {
202 for (int r = 0; r < patch_rows; ++r) {
203 for (int p = 0; p < patch_planes; ++p) {
204 for (int od = 0; od < output_depth; ++od) {
205 int output_i = i - p + forward_pad_z;
206 int output_j = j - r + forward_pad_y;
207 int output_k = k - c + forward_pad_x;
208 if (output_i >= 0 && output_i < output_planes &&
209 output_j >= 0 && output_j < output_rows &&
210 output_k >= 0 && output_k < output_cols) {
211 expected +=
212 output_backward(od, output_i, output_j, output_k) *
213 kernel(od, id, p, r, c);
214 }
215 }
216 }
217 }
218 }
219 EigenApprox(input_backward(id, i, j, k), expected);
220 }
221 }
222 }
223 }
224 }
225
TEST(EigenBackwardSpatialConvolutionsTest,test_simple_cuboid_convolution_backward_input_same_row_major)226 TEST(EigenBackwardSpatialConvolutionsTest,
227 test_simple_cuboid_convolution_backward_input_same_row_major) {
228 const int input_depth = 2;
229 const int input_planes = 5;
230 const int input_rows = 3;
231 const int input_cols = 4;
232 const int patch_rows = 2;
233 const int patch_cols = 3;
234 const int patch_planes = 4;
235 const int output_rows = input_rows;
236 const int output_cols = input_cols;
237 const int output_planes = input_planes;
238 const int output_depth = 5;
239
240 Tensor<float, 4, RowMajor> input_backward(input_cols, input_rows,
241 input_planes, input_depth);
242 Tensor<float, 5, RowMajor> kernel(patch_cols, patch_rows, patch_planes,
243 input_depth, output_depth);
244 Tensor<float, 4, RowMajor> output_backward(output_cols, output_rows,
245 output_planes, output_depth);
246
247 output_backward = output_backward.constant(11.0f) + output_backward.random();
248 kernel = kernel.constant(2.0f) + kernel.random();
249 input_backward.setRandom();
250
251 input_backward = CuboidConvolutionBackwardInput(
252 kernel, output_backward, input_planes, input_rows, input_cols);
253
254 EXPECT_EQ(input_backward.dimension(0), input_cols);
255 EXPECT_EQ(input_backward.dimension(1), input_rows);
256 EXPECT_EQ(input_backward.dimension(2), input_planes);
257 EXPECT_EQ(input_backward.dimension(3), input_depth);
258
259 const int dz = patch_planes - 1;
260 const int dy = patch_rows - 1;
261 const int dx = patch_cols - 1;
262
263 const int forward_pad_x = dx / 2;
264 const int forward_pad_y = dy / 2;
265 const int forward_pad_z = dz / 2;
266
267 for (int id = 0; id < input_depth; ++id) {
268 for (int i = 0; i < input_planes; ++i) {
269 for (int j = 0; j < input_rows; ++j) {
270 for (int k = 0; k < input_cols; ++k) {
271 float expected = 0.0f;
272 for (int c = 0; c < patch_cols; ++c) {
273 for (int r = 0; r < patch_rows; ++r) {
274 for (int p = 0; p < patch_planes; ++p) {
275 for (int od = 0; od < output_depth; ++od) {
276 int output_i = i - p + forward_pad_z;
277 int output_j = j - r + forward_pad_y;
278 int output_k = k - c + forward_pad_x;
279 if (output_i >= 0 && output_i < output_planes &&
280 output_j >= 0 && output_j < output_rows &&
281 output_k >= 0 && output_k < output_cols) {
282 expected +=
283 output_backward(output_k, output_j, output_i, od) *
284 kernel(c, r, p, id, od);
285 }
286 }
287 }
288 }
289 }
290 EigenApprox(input_backward(k, j, i, id), expected);
291 }
292 }
293 }
294 }
295 }
296
TEST(EigenBackwardSpatialConvolutionsTest,test_batched_cuboid_convolution_backward_input_valid)297 TEST(EigenBackwardSpatialConvolutionsTest,
298 test_batched_cuboid_convolution_backward_input_valid) {
299 const int num_batches = 13;
300 const int input_depth = 2;
301 const int input_planes = 5;
302 const int input_rows = 3;
303 const int input_cols = 4;
304 const int patch_rows = 2;
305 const int patch_cols = 2;
306 const int patch_planes = 2;
307 const int output_rows = input_rows - patch_rows + 1;
308 const int output_cols = input_cols - patch_cols + 1;
309 const int output_planes = input_planes - patch_planes + 1;
310 const int output_depth = 5;
311
312 Tensor<float, 5> input_backward(input_depth, input_planes, input_rows,
313 input_cols, num_batches);
314 Tensor<float, 5> kernel(output_depth, input_depth, patch_planes, patch_rows,
315 patch_cols);
316 Tensor<float, 5> output_backward(output_depth, output_planes, output_rows,
317 output_cols, num_batches);
318
319 output_backward = output_backward.constant(11.0f) + output_backward.random();
320 kernel = kernel.constant(2.0f) + kernel.random();
321 input_backward.setRandom();
322
323 input_backward = CuboidConvolutionBackwardInput(
324 kernel, output_backward, input_planes, input_rows, input_cols);
325
326 EXPECT_EQ(input_backward.dimension(4), num_batches);
327 EXPECT_EQ(input_backward.dimension(3), input_cols);
328 EXPECT_EQ(input_backward.dimension(2), input_rows);
329 EXPECT_EQ(input_backward.dimension(1), input_planes);
330 EXPECT_EQ(input_backward.dimension(0), input_depth);
331
332 for (int b = 0; b < num_batches; ++b) {
333 for (int id = 0; id < input_depth; ++id) {
334 for (int i = 0; i < input_planes; ++i) {
335 for (int j = 0; j < input_rows; ++j) {
336 for (int k = 0; k < input_cols; ++k) {
337 float expected = 0.0f;
338 for (int c = 0; c < patch_cols; ++c) {
339 for (int r = 0; r < patch_rows; ++r) {
340 for (int p = 0; p < patch_planes; ++p) {
341 for (int od = 0; od < output_depth; ++od) {
342 int output_i = i - p;
343 int output_j = j - r;
344 int output_k = k - c;
345 if (output_i >= 0 && output_i < output_planes &&
346 output_j >= 0 && output_j < output_rows &&
347 output_k >= 0 && output_k < output_cols) {
348 expected +=
349 output_backward(od, output_i, output_j, output_k, b) *
350 kernel(od, id, p, r, c);
351 }
352 }
353 }
354 }
355 }
356 EigenApprox(input_backward(id, i, j, k, b), expected);
357 }
358 }
359 }
360 }
361 }
362 }
363
TEST(EigenBackwardSpatialConvolutionsTest,test_batched_cuboid_convolution_backward_input_valid_row_major)364 TEST(EigenBackwardSpatialConvolutionsTest,
365 test_batched_cuboid_convolution_backward_input_valid_row_major) {
366 const int num_batches = 13;
367 const int input_depth = 2;
368 const int input_planes = 5;
369 const int input_rows = 3;
370 const int input_cols = 4;
371 const int patch_rows = 2;
372 const int patch_cols = 2;
373 const int patch_planes = 2;
374 const int output_rows = input_rows - patch_rows + 1;
375 const int output_cols = input_cols - patch_cols + 1;
376 const int output_planes = input_planes - patch_planes + 1;
377 const int output_depth = 5;
378
379 Tensor<float, 5, RowMajor> input_backward(num_batches, input_cols, input_rows,
380 input_planes, input_depth);
381 Tensor<float, 5, RowMajor> kernel(patch_cols, patch_rows, patch_planes,
382 input_depth, output_depth);
383 Tensor<float, 5, RowMajor> output_backward(
384 num_batches, output_cols, output_rows, output_planes, output_depth);
385
386 output_backward = output_backward.constant(11.0f) + output_backward.random();
387 kernel = kernel.constant(2.0f) + kernel.random();
388 input_backward.setRandom();
389
390 input_backward = CuboidConvolutionBackwardInput(
391 kernel, output_backward, input_planes, input_rows, input_cols);
392
393 EXPECT_EQ(input_backward.dimension(0), num_batches);
394 EXPECT_EQ(input_backward.dimension(1), input_cols);
395 EXPECT_EQ(input_backward.dimension(2), input_rows);
396 EXPECT_EQ(input_backward.dimension(3), input_planes);
397 EXPECT_EQ(input_backward.dimension(4), input_depth);
398
399 for (int b = 0; b < num_batches; ++b) {
400 for (int id = 0; id < input_depth; ++id) {
401 for (int i = 0; i < input_planes; ++i) {
402 for (int j = 0; j < input_rows; ++j) {
403 for (int k = 0; k < input_cols; ++k) {
404 float expected = 0.0f;
405 for (int c = 0; c < patch_cols; ++c) {
406 for (int r = 0; r < patch_rows; ++r) {
407 for (int p = 0; p < patch_planes; ++p) {
408 for (int od = 0; od < output_depth; ++od) {
409 int output_i = i - p;
410 int output_j = j - r;
411 int output_k = k - c;
412 if (output_i >= 0 && output_i < output_planes &&
413 output_j >= 0 && output_j < output_rows &&
414 output_k >= 0 && output_k < output_cols) {
415 expected +=
416 output_backward(b, output_k, output_j, output_i, od) *
417 kernel(c, r, p, id, od);
418 }
419 }
420 }
421 }
422 }
423 EigenApprox(input_backward(b, k, j, i, id), expected);
424 }
425 }
426 }
427 }
428 }
429 }
430
TEST(EigenBackwardSpatialConvolutionsTest,test_simple_cuboid_convolution_backward_kernel_valid)431 TEST(EigenBackwardSpatialConvolutionsTest,
432 test_simple_cuboid_convolution_backward_kernel_valid) {
433 const int input_depth = 2;
434 const int input_planes = 5;
435 const int input_rows = 3;
436 const int input_cols = 4;
437 const int output_depth = 5;
438 const int patch_rows = 2;
439 const int patch_cols = 2;
440 const int patch_planes = 3;
441 const int output_rows = input_rows - patch_rows + 1;
442 const int output_cols = input_cols - patch_cols + 1;
443 const int output_planes = input_planes - patch_planes + 1;
444
445 // TODO(ezhulenev): Support backward kernel convolution without batch
446 // dimension.
447 Tensor<float, 5> input(input_depth, input_planes, input_rows, input_cols,
448 /*num_batches*/ 1);
449 Tensor<float, 5> kernel(output_depth, input_depth, patch_planes, patch_rows,
450 patch_cols);
451 Tensor<float, 5> output_backward(output_depth, output_planes, output_rows,
452 output_cols, /*num_batches*/ 1);
453
454 output_backward = output_backward.constant(11.0f) + output_backward.random();
455 input = input.constant(2.0f) + input.random();
456 kernel.setRandom();
457
458 kernel = CuboidConvolutionBackwardKernel(input, output_backward, patch_planes,
459 patch_rows, patch_cols, 1, 1, 1);
460
461 EXPECT_EQ(kernel.dimension(0), output_depth);
462 EXPECT_EQ(kernel.dimension(1), input_depth);
463 EXPECT_EQ(kernel.dimension(2), patch_planes);
464 EXPECT_EQ(kernel.dimension(3), patch_rows);
465 EXPECT_EQ(kernel.dimension(4), patch_cols);
466
467 for (int od = 0; od < output_depth; ++od) {
468 for (int id = 0; id < input_depth; ++id) {
469 for (int p = 0; p < patch_planes; ++p) {
470 for (int r = 0; r < patch_rows; ++r) {
471 for (int c = 0; c < patch_cols; ++c) {
472 float expected = 0.0f;
473 for (int i = 0; i < input_planes; ++i) {
474 for (int j = 0; j < input_rows; ++j) {
475 for (int k = 0; k < input_cols; ++k) {
476 int output_j = j - r;
477 int output_k = k - c;
478 int output_i = i - p;
479 if (output_i >= 0 && output_i < output_planes &&
480 output_j >= 0 && output_j < output_rows &&
481 output_k >= 0 && output_k < output_cols) {
482 expected += input(id, i, j, k, /*batch*/ 0) *
483 output_backward(od, output_i, output_j,
484 output_k, /*batch*/ 0);
485 }
486 }
487 }
488 }
489 EigenApprox(kernel(od, id, p, r, c), expected);
490 }
491 }
492 }
493 }
494 }
495 }
496
TEST(EigenBackwardSpatialConvolutionsTest,test_simple_cuboid_convolution_backward_kernel_valid_row_major)497 TEST(EigenBackwardSpatialConvolutionsTest,
498 test_simple_cuboid_convolution_backward_kernel_valid_row_major) {
499 const int input_depth = 2;
500 const int input_planes = 5;
501 const int input_rows = 3;
502 const int input_cols = 4;
503 const int output_depth = 5;
504 const int patch_rows = 2;
505 const int patch_cols = 2;
506 const int patch_planes = 3;
507 const int output_rows = input_rows - patch_rows + 1;
508 const int output_cols = input_cols - patch_cols + 1;
509 const int output_planes = input_planes - patch_planes + 1;
510
511 // TODO(ezhulenev): Support backward kernel convolution without batch
512 // dimension.
513 Tensor<float, 5, RowMajor> input(/*num_batches*/ 1, input_cols, input_rows,
514 input_planes, input_depth);
515 Tensor<float, 5, RowMajor> kernel(patch_cols, patch_rows, patch_planes,
516 input_depth, output_depth);
517 Tensor<float, 5, RowMajor> output_backward(
518 /*num_batches*/ 1, output_cols, output_rows, output_planes, output_depth);
519
520 output_backward = output_backward.constant(11.0f) + output_backward.random();
521 input = input.constant(2.0f) + input.random();
522 kernel.setRandom();
523
524 kernel = CuboidConvolutionBackwardKernel(input, output_backward, patch_planes,
525 patch_rows, patch_cols, 1, 1, 1);
526
527 EXPECT_EQ(kernel.dimension(4), output_depth);
528 EXPECT_EQ(kernel.dimension(3), input_depth);
529 EXPECT_EQ(kernel.dimension(2), patch_planes);
530 EXPECT_EQ(kernel.dimension(1), patch_rows);
531 EXPECT_EQ(kernel.dimension(0), patch_cols);
532
533 for (int od = 0; od < output_depth; ++od) {
534 for (int id = 0; id < input_depth; ++id) {
535 for (int p = 0; p < patch_planes; ++p) {
536 for (int r = 0; r < patch_rows; ++r) {
537 for (int c = 0; c < patch_cols; ++c) {
538 float expected = 0.0f;
539 for (int i = 0; i < input_planes; ++i) {
540 for (int j = 0; j < input_rows; ++j) {
541 for (int k = 0; k < input_cols; ++k) {
542 int output_j = j - r;
543 int output_k = k - c;
544 int output_i = i - p;
545 if (output_i >= 0 && output_i < output_planes &&
546 output_j >= 0 && output_j < output_rows &&
547 output_k >= 0 && output_k < output_cols) {
548 expected += input(/*batch*/ 0, k, j, i, id) *
549 output_backward(/*batch*/ 0, output_k, output_j,
550 output_i, od);
551 }
552 }
553 }
554 }
555 EigenApprox(kernel(c, r, p, id, od), expected);
556 }
557 }
558 }
559 }
560 }
561 }
562
TEST(EigenBackwardSpatialConvolutionsTest,test_batched_cuboid_convolution_backward_kernel_valid)563 TEST(EigenBackwardSpatialConvolutionsTest,
564 test_batched_cuboid_convolution_backward_kernel_valid) {
565 const int num_batches = 13;
566 const int input_depth = 2;
567 const int input_planes = 5;
568 const int input_rows = 7;
569 const int input_cols = 9;
570 const int output_depth = 3;
571 const int patch_rows = 5;
572 const int patch_cols = 5;
573 const int patch_planes = 3;
574 const int output_rows = input_rows - patch_rows + 1;
575 const int output_cols = input_cols - patch_cols + 1;
576 const int output_planes = input_planes - patch_planes + 1;
577
578 Tensor<float, 5> input(input_depth, input_planes, input_rows, input_cols,
579 num_batches);
580 Tensor<float, 5> kernel_backward(output_depth, input_depth, patch_planes,
581 patch_rows, patch_cols);
582 Tensor<float, 5> output_backward(output_depth, output_planes, output_rows,
583 output_cols, num_batches);
584
585 output_backward = output_backward.constant(11.0f) + output_backward.random();
586 input = input.constant(2.0f) + input.random();
587 kernel_backward.setRandom();
588
589 kernel_backward = CuboidConvolutionBackwardKernel(
590 input, output_backward, patch_planes, patch_rows, patch_cols, 1, 1, 1);
591
592 EXPECT_EQ(kernel_backward.dimension(0), output_depth);
593 EXPECT_EQ(kernel_backward.dimension(1), input_depth);
594 EXPECT_EQ(kernel_backward.dimension(2), patch_planes);
595 EXPECT_EQ(kernel_backward.dimension(3), patch_rows);
596 EXPECT_EQ(kernel_backward.dimension(4), patch_cols);
597
598 for (int od = 0; od < output_depth; ++od) {
599 for (int id = 0; id < input_depth; ++id) {
600 for (int p = 0; p < patch_planes; ++p) {
601 for (int c = 0; c < patch_cols; ++c) {
602 for (int r = 0; r < patch_rows; ++r) {
603 float expected = 0.0f;
604 for (int b = 0; b < num_batches; ++b) {
605 for (int i = 0; i < input_planes; ++i) {
606 for (int j = 0; j < input_rows; ++j) {
607 for (int k = 0; k < input_cols; ++k) {
608 int output_j = j - r;
609 int output_k = k - c;
610 int output_i = i - p;
611 if (output_i >= 0 && output_i < output_planes &&
612 output_j >= 0 && output_j < output_rows &&
613 output_k >= 0 && output_k < output_cols) {
614 expected +=
615 input(id, i, j, k, b) *
616 output_backward(od, output_i, output_j, output_k, b);
617 }
618 }
619 }
620 }
621 }
622 EigenApprox(kernel_backward(od, id, p, r, c), expected);
623 }
624 }
625 }
626 }
627 }
628 }
629
TEST(EigenBackwardSpatialConvolutionsTest,test_batched_cuboid_convolution_backward_kernel_valid_row_major)630 TEST(EigenBackwardSpatialConvolutionsTest,
631 test_batched_cuboid_convolution_backward_kernel_valid_row_major) {
632 const int num_batches = 13;
633 const int input_depth = 2;
634 const int input_planes = 5;
635 const int input_rows = 7;
636 const int input_cols = 9;
637 const int output_depth = 3;
638 const int patch_rows = 5;
639 const int patch_cols = 5;
640 const int patch_planes = 3;
641 const int output_rows = input_rows - patch_rows + 1;
642 const int output_cols = input_cols - patch_cols + 1;
643 const int output_planes = input_planes - patch_planes + 1;
644
645 Tensor<float, 5, RowMajor> input(num_batches, input_cols, input_rows,
646 input_planes, input_depth);
647 Tensor<float, 5, RowMajor> kernel_backward(
648 patch_cols, patch_rows, patch_planes, input_depth, output_depth);
649 Tensor<float, 5, RowMajor> output_backward(
650 num_batches, output_cols, output_rows, output_planes, output_depth);
651
652 output_backward = output_backward.constant(11.0f) + output_backward.random();
653 input = input.constant(2.0f) + input.random();
654 kernel_backward.setRandom();
655
656 kernel_backward = CuboidConvolutionBackwardKernel(
657 input, output_backward, patch_planes, patch_rows, patch_cols, 1, 1, 1);
658
659 EXPECT_EQ(kernel_backward.dimension(4), output_depth);
660 EXPECT_EQ(kernel_backward.dimension(3), input_depth);
661 EXPECT_EQ(kernel_backward.dimension(2), patch_planes);
662 EXPECT_EQ(kernel_backward.dimension(1), patch_rows);
663 EXPECT_EQ(kernel_backward.dimension(0), patch_cols);
664
665 for (int od = 0; od < output_depth; ++od) {
666 for (int id = 0; id < input_depth; ++id) {
667 for (int p = 0; p < patch_planes; ++p) {
668 for (int c = 0; c < patch_cols; ++c) {
669 for (int r = 0; r < patch_rows; ++r) {
670 float expected = 0.0f;
671 for (int b = 0; b < num_batches; ++b) {
672 for (int i = 0; i < input_planes; ++i) {
673 for (int j = 0; j < input_rows; ++j) {
674 for (int k = 0; k < input_cols; ++k) {
675 int output_j = j - r;
676 int output_k = k - c;
677 int output_i = i - p;
678 if (output_i >= 0 && output_i < output_planes &&
679 output_j >= 0 && output_j < output_rows &&
680 output_k >= 0 && output_k < output_cols) {
681 expected +=
682 input(b, k, j, i, id) *
683 output_backward(b, output_k, output_j, output_i, od);
684 }
685 }
686 }
687 }
688 }
689 EigenApprox(kernel_backward(c, r, p, id, od), expected);
690 }
691 }
692 }
693 }
694 }
695 }
696
TEST(EigenBackwardSpatialConvolutionsTest,test_batched_strided_cuboid_convolution_backward_kernel_valid)697 TEST(EigenBackwardSpatialConvolutionsTest,
698 test_batched_strided_cuboid_convolution_backward_kernel_valid) {
699 const int num_batches = 13;
700 const int input_depth = 2;
701 const int input_planes = 8;
702 const int input_rows = 7;
703 const int input_cols = 9;
704 const int output_depth = 3;
705 const int patch_planes = 3;
706 const int patch_rows = 3;
707 const int patch_cols = 2;
708
709 const int stride_planes = 2;
710 const int stride_cols = 3;
711 const int stride_rows = 1;
712
713 const int output_rows = ceil_div(input_rows - patch_rows + 1, stride_rows);
714 const int output_cols = ceil_div(input_cols - patch_cols + 1, stride_cols);
715 const int output_planes =
716 ceil_div(input_planes - patch_planes + 1, stride_planes);
717
718 Tensor<float, 5> input(input_depth, input_planes, input_rows, input_cols,
719 num_batches);
720 Tensor<float, 5> kernel_backward(output_depth, input_depth, patch_planes,
721 patch_rows, patch_cols);
722 Tensor<float, 5> output_backward(output_depth, output_planes, output_rows,
723 output_cols, num_batches);
724
725 output_backward = output_backward.constant(11.0f) + output_backward.random();
726 input = input.constant(2.0f) + input.random();
727 kernel_backward.setRandom();
728
729 kernel_backward = CuboidConvolutionBackwardKernel(
730 input, output_backward, patch_planes, patch_rows, patch_cols,
731 stride_planes, stride_rows, stride_cols);
732
733 EXPECT_EQ(kernel_backward.dimension(0), output_depth);
734 EXPECT_EQ(kernel_backward.dimension(1), input_depth);
735 EXPECT_EQ(kernel_backward.dimension(2), patch_planes);
736 EXPECT_EQ(kernel_backward.dimension(3), patch_rows);
737 EXPECT_EQ(kernel_backward.dimension(4), patch_cols);
738
739 for (int od = 0; od < output_depth; ++od) {
740 for (int id = 0; id < input_depth; ++id) {
741 for (int p = 0; p < patch_planes; ++p) {
742 for (int c = 0; c < patch_cols; ++c) {
743 for (int r = 0; r < patch_rows; ++r) {
744 float expected = 0.0f;
745 for (int b = 0; b < num_batches; ++b) {
746 for (int i = 0; i < input_planes; ++i) {
747 for (int j = 0; j < input_rows; ++j) {
748 for (int k = 0; k < input_cols; ++k) {
749 int output_j = j - r;
750 int output_k = k - c;
751 int output_i = i - p;
752 if (output_i >= 0 &&
753 output_i / stride_planes < output_planes &&
754 output_j >= 0 && output_j / stride_rows < output_rows &&
755 output_k >= 0 && output_k / stride_cols < output_cols &&
756 output_i % stride_planes == 0 &&
757 output_j % stride_rows == 0 &&
758 output_k % stride_cols == 0) {
759 expected += input(id, i, j, k, b) *
760 output_backward(od, output_i / stride_planes,
761 output_j / stride_rows,
762 output_k / stride_cols, b);
763 }
764 }
765 }
766 }
767 }
768 EigenApprox(kernel_backward(od, id, p, r, c), expected);
769 }
770 }
771 }
772 }
773 }
774 }
775
TEST(EigenBackwardSpatialConvolutionsTest,test_batched_strided_cuboid_convolution_backward_kernel_valid_row_major)776 TEST(EigenBackwardSpatialConvolutionsTest,
777 test_batched_strided_cuboid_convolution_backward_kernel_valid_row_major) {
778 const int num_batches = 13;
779 const int input_depth = 2;
780 const int input_planes = 8;
781 const int input_rows = 7;
782 const int input_cols = 9;
783 const int output_depth = 3;
784 const int patch_planes = 3;
785 const int patch_rows = 3;
786 const int patch_cols = 2;
787
788 const int stride_planes = 2;
789 const int stride_cols = 3;
790 const int stride_rows = 1;
791
792 const int output_rows = ceil_div(input_rows - patch_rows + 1, stride_rows);
793 const int output_cols = ceil_div(input_cols - patch_cols + 1, stride_cols);
794 const int output_planes =
795 ceil_div(input_planes - patch_planes + 1, stride_planes);
796
797 Tensor<float, 5, RowMajor> input(num_batches, input_cols, input_rows,
798 input_planes, input_depth);
799 Tensor<float, 5, RowMajor> kernel_backward(
800 patch_cols, patch_rows, patch_planes, input_depth, output_depth);
801 Tensor<float, 5, RowMajor> output_backward(
802 num_batches, output_cols, output_rows, output_planes, output_depth);
803
804 output_backward = output_backward.constant(11.0f) + output_backward.random();
805 input = input.constant(2.0f) + input.random();
806 kernel_backward.setRandom();
807
808 kernel_backward = CuboidConvolutionBackwardKernel(
809 input, output_backward, patch_planes, patch_rows, patch_cols,
810 stride_planes, stride_rows, stride_cols);
811
812 EXPECT_EQ(kernel_backward.dimension(4), output_depth);
813 EXPECT_EQ(kernel_backward.dimension(3), input_depth);
814 EXPECT_EQ(kernel_backward.dimension(2), patch_planes);
815 EXPECT_EQ(kernel_backward.dimension(1), patch_rows);
816 EXPECT_EQ(kernel_backward.dimension(0), patch_cols);
817
818 for (int od = 0; od < output_depth; ++od) {
819 for (int id = 0; id < input_depth; ++id) {
820 for (int p = 0; p < patch_planes; ++p) {
821 for (int c = 0; c < patch_cols; ++c) {
822 for (int r = 0; r < patch_rows; ++r) {
823 float expected = 0.0f;
824 for (int b = 0; b < num_batches; ++b) {
825 for (int i = 0; i < input_planes; ++i) {
826 for (int j = 0; j < input_rows; ++j) {
827 for (int k = 0; k < input_cols; ++k) {
828 int output_j = j - r;
829 int output_k = k - c;
830 int output_i = i - p;
831 if (output_i >= 0 &&
832 output_i / stride_planes < output_planes &&
833 output_j >= 0 && output_j / stride_rows < output_rows &&
834 output_k >= 0 && output_k / stride_cols < output_cols &&
835 output_i % stride_planes == 0 &&
836 output_j % stride_rows == 0 &&
837 output_k % stride_cols == 0) {
838 expected += input(b, k, j, i, id) *
839 output_backward(b, output_k / stride_cols,
840 output_j / stride_rows,
841 output_i / stride_planes, od);
842 }
843 }
844 }
845 }
846 }
847 EigenApprox(kernel_backward(c, r, p, id, od), expected);
848 }
849 }
850 }
851 }
852 }
853 }
854
TEST(EigenBackwardSpatialConvolutionsTest,test_batched_strided_cuboid_convolution_backward_input_valid)855 TEST(EigenBackwardSpatialConvolutionsTest,
856 test_batched_strided_cuboid_convolution_backward_input_valid) {
857 const int num_batches = 13;
858 const int input_depth = 2;
859 const int input_planes = 14;
860 const int input_rows = 13;
861 const int input_cols = 15;
862 const int patch_rows = 3;
863 const int patch_cols = 2;
864 const int patch_planes = 4;
865 const int stride_rows = 3;
866 const int stride_cols = 2;
867 const int stride_planes = 3;
868 const int output_rows = ceil_div(input_rows - patch_rows + 1, stride_rows);
869 const int output_cols = ceil_div(input_cols - patch_cols + 1, stride_cols);
870 const int output_planes =
871 ceil_div(input_planes - patch_planes + 1, stride_planes);
872 const int output_depth = 5;
873
874 Tensor<float, 5> input_backward(input_depth, input_planes, input_rows,
875 input_cols, num_batches);
876 Tensor<float, 5> kernel(output_depth, input_depth, patch_planes, patch_rows,
877 patch_cols);
878 Tensor<float, 5> output_backward(output_depth, output_planes, output_rows,
879 output_cols, num_batches);
880
881 output_backward = output_backward.constant(11.0f) + output_backward.random();
882 kernel = kernel.constant(2.0f) + kernel.random();
883 input_backward.setRandom();
884
885 input_backward = CuboidConvolutionBackwardInput(
886 kernel, output_backward, input_planes, input_rows, input_cols,
887 stride_planes, stride_rows, stride_cols);
888
889 EXPECT_EQ(input_backward.dimension(4), num_batches);
890 EXPECT_EQ(input_backward.dimension(3), input_cols);
891 EXPECT_EQ(input_backward.dimension(2), input_rows);
892 EXPECT_EQ(input_backward.dimension(1), input_planes);
893 EXPECT_EQ(input_backward.dimension(0), input_depth);
894
895 for (int b = 0; b < num_batches; ++b) {
896 for (int id = 0; id < input_depth; ++id) {
897 for (int i = 0; i < input_planes; ++i) {
898 for (int j = 0; j < input_rows; ++j) {
899 for (int k = 0; k < input_cols; ++k) {
900 float expected = 0.0f;
901 for (int c = 0; c < patch_cols; ++c) {
902 for (int r = 0; r < patch_rows; ++r) {
903 for (int p = 0; p < patch_planes; ++p) {
904 for (int od = 0; od < output_depth; ++od) {
905 int output_j = j - r;
906 int output_k = k - c;
907 int output_i = i - p;
908 if (output_i >= 0 &&
909 output_i / stride_planes < output_planes &&
910 output_j >= 0 && output_j / stride_rows < output_rows &&
911 output_k >= 0 && output_k / stride_cols < output_cols &&
912 output_i % stride_planes == 0 &&
913 output_j % stride_rows == 0 &&
914 output_k % stride_cols == 0) {
915 expected += output_backward(od, output_i / stride_planes,
916 output_j / stride_rows,
917 output_k / stride_cols, b) *
918 kernel(od, id, p, r, c);
919 }
920 }
921 }
922 }
923 }
924 EigenApprox(input_backward(id, i, j, k, b), expected);
925 }
926 }
927 }
928 }
929 }
930 }
931
TEST(EigenBackwardSpatialConvolutionsTest,test_batched_strided_cuboid_convolution_backward_input_valid_row_major)932 TEST(EigenBackwardSpatialConvolutionsTest,
933 test_batched_strided_cuboid_convolution_backward_input_valid_row_major) {
934 const int num_batches = 13;
935 const int input_depth = 2;
936 const int input_planes = 14;
937 const int input_rows = 13;
938 const int input_cols = 15;
939 const int patch_rows = 3;
940 const int patch_cols = 2;
941 const int patch_planes = 4;
942 const int stride_rows = 3;
943 const int stride_cols = 2;
944 const int stride_planes = 3;
945 const int output_rows = ceil_div(input_rows - patch_rows + 1, stride_rows);
946 const int output_cols = ceil_div(input_cols - patch_cols + 1, stride_cols);
947 const int output_planes =
948 ceil_div(input_planes - patch_planes + 1, stride_planes);
949 const int output_depth = 5;
950
951 Tensor<float, 5, RowMajor> input_backward(num_batches, input_cols, input_rows,
952 input_planes, input_depth);
953 Tensor<float, 5, RowMajor> kernel(patch_cols, patch_rows, patch_planes,
954 input_depth, output_depth);
955 Tensor<float, 5, RowMajor> output_backward(
956 num_batches, output_cols, output_rows, output_planes, output_depth);
957
958 output_backward = output_backward.constant(11.0f) + output_backward.random();
959 kernel = kernel.constant(2.0f) + kernel.random();
960 input_backward.setRandom();
961
962 input_backward = CuboidConvolutionBackwardInput(
963 kernel, output_backward, input_planes, input_rows, input_cols,
964 stride_planes, stride_rows, stride_cols);
965
966 EXPECT_EQ(input_backward.dimension(0), num_batches);
967 EXPECT_EQ(input_backward.dimension(1), input_cols);
968 EXPECT_EQ(input_backward.dimension(2), input_rows);
969 EXPECT_EQ(input_backward.dimension(3), input_planes);
970 EXPECT_EQ(input_backward.dimension(4), input_depth);
971
972 for (int b = 0; b < num_batches; ++b) {
973 for (int id = 0; id < input_depth; ++id) {
974 for (int i = 0; i < input_planes; ++i) {
975 for (int j = 0; j < input_rows; ++j) {
976 for (int k = 0; k < input_cols; ++k) {
977 float expected = 0.0f;
978 for (int c = 0; c < patch_cols; ++c) {
979 for (int r = 0; r < patch_rows; ++r) {
980 for (int p = 0; p < patch_planes; ++p) {
981 for (int od = 0; od < output_depth; ++od) {
982 int output_j = j - r;
983 int output_k = k - c;
984 int output_i = i - p;
985 if (output_i >= 0 &&
986 output_i / stride_planes < output_planes &&
987 output_j >= 0 && output_j / stride_rows < output_rows &&
988 output_k >= 0 && output_k / stride_cols < output_cols &&
989 output_i % stride_planes == 0 &&
990 output_j % stride_rows == 0 &&
991 output_k % stride_cols == 0) {
992 expected +=
993 output_backward(b, output_k / stride_cols,
994 output_j / stride_rows,
995 output_i / stride_planes, od) *
996 kernel(c, r, p, id, od);
997 }
998 }
999 }
1000 }
1001 }
1002 EigenApprox(input_backward(b, k, j, i, id), expected);
1003 }
1004 }
1005 }
1006 }
1007 }
1008 }
1009
1010 } // namespace Eigen
1011