1 /*******************************************************************************
2 * Copyright 2018 Intel Corporation
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *******************************************************************************/
17 #include "mkldnn_test_common.hpp"
18 #include "gtest/gtest.h"
21 #include "mkldnn_debug.h"
23 using fmt = memory::format;
24 struct deconvolution_test_params {
25 const mkldnn::engine::kind engine_kind;
26 mkldnn::algorithm aalgorithm;
27 test_convolution_formats_t formats;
28 test_convolution_attr_t attr;
29 test_convolution_sizes_t sizes;
31 mkldnn_status_t expected_status;
33 template <typename data_t>
34 void compute_bias_fwd(const test_convolution_sizes_t &c,
35 mkldnn::memory& dst, mkldnn::memory& bias) {
36 data_t *bias_data = (data_t *)bias.get_data_handle();
37 data_t *dst_data = (data_t *)dst.get_data_handle();
39 const memory::desc bias_d = bias.get_primitive_desc().desc();
40 const memory::desc dst_d = dst.get_primitive_desc().desc();
42 mkldnn::impl::parallel_nd(c.mb, c.ng, c.oc / c.ng, c.oh, c.ow,
43 [&](int n, int g, int oc, int oh, int ow) {
44 data_t b = bias_data[map_index(bias_d, g * c.oc / c.ng + oc)];
45 int oidx = n * c.oc * c.oh * c.ow
46 + g * c.oc / c.ng * c.oh * c.ow
47 + oc * c.oh * c.ow + oh * c.ow + ow;
48 dst_data[map_index(dst_d, oidx)] += b;
53 template <typename data_t>
54 void compute_bias_bwd(const test_convolution_sizes_t &c,
55 mkldnn::memory& dst, mkldnn::memory& bias) {
56 data_t *bias_data = (data_t *)bias.get_data_handle();
57 data_t *dst_data = (data_t *)dst.get_data_handle();
59 const memory::desc bias_d = bias.get_primitive_desc().desc();
60 const memory::desc dst_d = dst.get_primitive_desc().desc();
62 mkldnn::impl::parallel_nd(c.ng, c.oc / c.ng, [&](int g, int oc) {
63 int bidx = g * c.oc / c.ng + oc;
64 bias_data[map_index(bias_d, bidx)] = 0.0;
65 for (int mb = 0; mb < c.mb; ++mb) {
66 for (int oh = 0; oh < c.oh; ++oh) {
67 for (int ow = 0; ow < c.ow; ++ow) {
68 int oidx = mb * c.oc * c.oh * c.ow
69 + g * c.oc / c.ng * c.oh * c.ow
70 + oc * c.oh * c.ow + oh * c.ow + ow;
71 bias_data[map_index(bias_d, bidx)]
72 += dst_data[map_index(dst_d, oidx)];
79 template <typename data_t>
80 void transpose_wei(const test_convolution_sizes_t &c,
81 mkldnn::memory& weights, mkldnn::memory& weights_tr) {
83 data_t *weights_data = (data_t *)weights.get_data_handle();
84 const memory::desc weights_d = weights.get_primitive_desc().desc();
85 data_t *weights_tr_data = (data_t *)weights_tr.get_data_handle();
86 const memory::desc weights_tr_d = weights_tr.get_primitive_desc().desc();
88 mkldnn::impl::parallel_nd(c.ng, c.oc / c.ng, c.ic / c.ng, c.kh, c.kw,
89 [&](int g, int oc, int ic, int kh, int kw) {
90 int widx = g * c.oc / c.ng * c.ic / c.ng * c.kh * c.kw
91 + oc * c.ic / c.ng * c.kh * c.kw
92 + ic * c.kh * c.kw + kh * c.kw + kw;
93 int widx_tr = g * c.oc / c.ng * c.ic / c.ng * c.kh * c.kw
94 + ic * c.oc / c.ng * c.kh * c.kw
95 + oc * c.kh * c.kw + kh * c.kw + kw;
96 weights_tr_data[map_index(weights_tr_d, widx_tr)]
97 = weights_data[map_index(weights_d, widx)];
102 template <typename data_t>
103 class deconvolution_test : public
104 ::testing::TestWithParam<deconvolution_test_params> {
106 std::shared_ptr<test_memory> src;
107 std::shared_ptr<test_memory> weights;
108 std::shared_ptr<test_memory> dst;
109 std::shared_ptr<test_memory> bias;
111 std::shared_ptr<memory::desc> dec_src_desc;
112 std::shared_ptr<memory::desc> dec_weights_desc;
113 std::shared_ptr<memory::desc> dec_bias_desc;
114 std::shared_ptr<memory::desc> dec_dst_desc;
116 std::shared_ptr<memory::desc> con_src_desc;
117 std::shared_ptr<memory::desc> con_bias_desc;
118 std::shared_ptr<memory::desc> con_dst_desc;
119 std::shared_ptr<memory::desc> con_weights_desc;
121 std::shared_ptr<engine> eng;
123 std::vector<ptrdiff_t> padR;
125 virtual void SetUp() {
126 auto p = ::testing::TestWithParam<deconvolution_test_params>::GetParam();
127 catch_expected_failures([=](){Test();}, p.expect_to_fail,
132 auto p = ::testing::TestWithParam<deconvolution_test_params>::GetParam();
134 ASSERT_TRUE(p.engine_kind == engine::kind::cpu);
135 eng.reset(new engine(p.engine_kind, 0));
137 ASSERT_EQ(p.aalgorithm, algorithm::deconvolution_direct);
138 memory::data_type data_type = data_traits<data_t>::data_type;
140 test_convolution_sizes_t dd = p.sizes;
141 p.formats.bias_format = memory::format::format_undef;
142 with_bias = p.formats.bias_format != memory::format::format_undef;
144 memory::dims src_dims = {dd.mb, dd.ic, dd.ih, dd.iw};
145 memory::dims dst_dims = {dd.mb, dd.oc, dd.oh, dd.ow};
146 memory::dims weights_dims, c_weights_dims;
148 weights_dims = { dd.ng, dd.oc / dd.ng, dd.ic / dd.ng, dd.kh, dd.kw };
149 c_weights_dims = { dd.ng, dd.ic / dd.ng, dd.oc / dd.ng, dd.kh, dd.kw };
151 weights_dims = { dd.oc, dd.ic, dd.kh, dd.kw };
152 c_weights_dims = { dd.ic, dd.oc, dd.kh, dd.kw };
154 memory::dims bias_dims;
155 if (with_bias) bias_dims = {dd.oc};
158 dec_src_desc.reset(new memory::desc(src_dims, data_type,
159 p.formats.src_format));
160 dec_dst_desc.reset(new memory::desc(dst_dims, data_type,
161 p.formats.src_format));
162 dec_weights_desc.reset(new memory::desc(weights_dims, data_type,
163 p.formats.weights_format));
164 dec_bias_desc.reset(new memory::desc(bias_dims, data_type,
165 p.formats.bias_format));
167 con_src_desc.reset(new memory::desc(dst_dims, data_type,
168 p.formats.src_format));
169 con_dst_desc.reset(new memory::desc(src_dims, data_type,
170 p.formats.src_format));
171 con_weights_desc.reset(new memory::desc(c_weights_dims, data_type,
172 p.formats.weights_format));
174 src.reset(new test_memory(*dec_src_desc, *eng));
175 weights.reset(new test_memory(*dec_weights_desc, *eng));
176 bias.reset(new test_memory(*dec_bias_desc, *eng));
177 dst.reset(new test_memory(*dec_dst_desc, *eng));
180 right_padding(dd.oh, dd.ih, dd.kh, dd.padh, dd.strh, dd.dilh),
181 right_padding(dd.ow, dd.iw, dd.kw, dd.padw, dd.strw, dd.dilw)
188 auto aprop_kind = prop_kind::forward;
189 deconvolution_test_params p =
190 ::testing::TestWithParam<deconvolution_test_params>::GetParam();
191 auto conv_src = test_memory(*con_src_desc, *eng);
193 test_convolution_sizes_t dd = p.sizes;
195 fill_data<data_t>(src->get_size() / sizeof(data_t),
196 (data_t *)src->get().get_data_handle());
198 fill_data<data_t>(weights->get_size() / sizeof(data_t),
199 (data_t *)weights->get().get_data_handle());
201 fill_data<data_t>(bias->get_size() / sizeof(data_t),
202 (data_t *)bias->get().get_data_handle());
205 auto weights_tr = memory({*con_weights_desc, *eng});
206 transpose_wei<data_t>(dd, weights->get(), weights_tr);
207 auto deconv_desc = with_bias ?
208 deconvolution_forward::desc(aprop_kind,
209 algorithm::deconvolution_direct, *dec_src_desc,
210 *dec_weights_desc, *dec_bias_desc, *dec_dst_desc,
211 { dd.strh, dd.strw }, { dd.padh, dd.padw }, padR,
212 padding_kind::zero) :
213 deconvolution_forward::desc(aprop_kind,
214 algorithm::deconvolution_direct, *dec_src_desc,
215 *dec_weights_desc, *dec_dst_desc, { dd.strh, dd.strw },
216 { dd.padh, dd.padw }, padR, padding_kind::zero);
218 auto deconv_primitive_desc = deconvolution_forward::primitive_desc(
221 auto deconv = with_bias ?
222 deconvolution_forward(deconv_primitive_desc, src->get(),
223 weights->get(), bias->get(), dst->get()) :
224 deconvolution_forward(deconv_primitive_desc, src->get(),
225 weights->get(), dst->get());
227 auto conv_desc = convolution_forward::desc(
228 prop_kind::forward_training, algorithm::convolution_direct,
229 *con_src_desc, *con_weights_desc, *con_dst_desc,
230 { dd.strh, dd.strw }, { dd.padh, dd.padw }, padR,
233 auto conv_primitive_desc = convolution_forward::primitive_desc(
236 auto conv_bwd_data_desc = convolution_backward_data::desc(
237 algorithm::convolution_direct, *con_src_desc,
238 *con_weights_desc, *con_dst_desc,
239 { dd.strh, dd.strw }, { dd.padh, dd.padw }, padR,
242 auto conv_bwd_data_primitive_desc
243 = convolution_backward_data::primitive_desc(
244 conv_bwd_data_desc, *eng, conv_primitive_desc);
246 auto conv_bwd_data = convolution_backward_data(
247 conv_bwd_data_primitive_desc,
248 conv_dst->get(), weights_tr, conv_src.get());
250 std::vector<primitive> pipeline;
251 pipeline.push_back(deconv);
252 pipeline.push_back(conv_bwd_data);
253 stream(stream::kind::lazy).submit(pipeline).wait();
255 if(with_bias) compute_bias_fwd<data_t>(dd, conv_src.get(), bias->get());
256 compare_data<data_t>(conv_src.get(), dst->get());
259 void BackwardData() {
260 auto p = ::testing::TestWithParam<deconvolution_test_params>::GetParam();
262 auto conv_dst = test_memory(*con_dst_desc, *eng);
263 test_convolution_sizes_t dd = p.sizes;
265 fill_data<data_t>(weights->get_size() / sizeof(data_t),
266 (data_t *)weights->get().get_data_handle());
268 fill_data<data_t>(dst->get_size() / sizeof(data_t),
269 (data_t *)dst->get().get_data_handle());
271 auto weights_tr = memory({*con_weights_desc, *eng});
272 transpose_wei<data_t>(dd, weights->get(), weights_tr);
274 auto deconv_desc = deconvolution_forward::desc(prop_kind::forward_training,
275 algorithm::deconvolution_direct, *dec_src_desc,
276 *dec_weights_desc, *dec_dst_desc, { dd.strh, dd.strw },
277 { dd.padh, dd.padw }, padR, padding_kind::zero);
279 auto deconv_primitive_desc = deconvolution_forward::primitive_desc(
282 auto deconv_bwd_data_desc = deconvolution_backward_data::desc(
283 algorithm::deconvolution_direct, *dec_src_desc,
284 *dec_weights_desc, *dec_dst_desc,
285 { dd.strh, dd.strw }, { dd.padh, dd.padw }, padR,
287 auto deconv_bwd_data_primitive_desc
288 = deconvolution_backward_data::primitive_desc(
289 deconv_bwd_data_desc, *eng, deconv_primitive_desc);
291 auto deconv_bwd_data = deconvolution_backward_data(
292 deconv_bwd_data_primitive_desc, dst->get(), weights->get(),
295 auto conv_desc = convolution_forward::desc(
296 prop_kind::forward_training, algorithm::convolution_direct,
297 *con_src_desc, *con_weights_desc, *con_dst_desc,
298 { dd.strh, dd.strw }, { dd.padh, dd.padw }, padR,
301 auto conv_primitive_desc = convolution_forward::primitive_desc(
304 auto conv = convolution_forward(conv_primitive_desc, conv_src->get(),
305 weights_tr, conv_dst.get());
307 std::vector<primitive> pipeline;
308 pipeline.push_back(deconv_bwd_data);
309 pipeline.push_back(conv);
310 stream(stream::kind::lazy).submit(pipeline).wait();
312 compare_data<data_t>(conv_dst.get(), src->get());
315 void BackwardWeights() {
316 auto p = ::testing::TestWithParam<deconvolution_test_params>::GetParam();
319 auto conv_weights = memory({*con_weights_desc, *eng});
320 test_convolution_sizes_t dd = p.sizes;
322 fill_data<data_t>(src->get_size() / sizeof(data_t),
323 (data_t *)src->get().get_data_handle());
325 fill_data<data_t>(dst->get_size() / sizeof(data_t),
326 (data_t *)dst->get().get_data_handle());
328 auto deconv_desc = deconvolution_forward::desc(prop_kind::forward_training,
329 algorithm::deconvolution_direct, *dec_src_desc,
330 *dec_weights_desc, *dec_bias_desc, *dec_dst_desc,
331 { dd.strh, dd.strw }, { dd.padh, dd.padw }, padR, padding_kind::zero);
333 auto deconv_primitive_desc = deconvolution_forward::primitive_desc(
336 auto deconv_bwd_weights_desc = deconvolution_backward_weights::desc(
337 algorithm::deconvolution_direct, *dec_src_desc,
338 *dec_weights_desc, *dec_bias_desc, *dec_dst_desc,
339 { dd.strh, dd.strw }, { dd.padh, dd.padw }, padR,
341 auto deconv_bwd_weights_primitive_desc
342 = deconvolution_backward_weights::primitive_desc(
343 deconv_bwd_weights_desc, *eng, deconv_primitive_desc);
345 auto deconv_bwd_weights = deconvolution_backward_weights(
346 deconv_bwd_weights_primitive_desc, src->get(), dst->get(),
347 weights->get(), bias->get());
349 auto conv_desc = convolution_forward::desc(
350 prop_kind::forward_training, algorithm::convolution_direct,
351 *con_src_desc, *con_weights_desc, *con_dst_desc,
352 { dd.strh, dd.strw }, { dd.padh, dd.padw }, padR,
355 auto conv_primitive_desc = convolution_forward::primitive_desc(
358 auto conv_bwd_weights_desc = convolution_backward_weights::desc(
359 algorithm::convolution_direct, *con_src_desc, *con_weights_desc,
360 *con_dst_desc, { dd.strh, dd.strw }, { dd.padh, dd.padw },
361 padR, padding_kind::zero);
363 auto conv_bwd_weights_primitive_desc =
364 convolution_backward_weights::primitive_desc(
365 conv_bwd_weights_desc, *eng, conv_primitive_desc);
367 auto conv_bwd_weights =
368 convolution_backward_weights(conv_bwd_weights_primitive_desc,
369 conv_src->get(), conv_dst->get(), conv_weights);
371 std::vector<primitive> pipeline;
372 pipeline.push_back(conv_bwd_weights);
373 pipeline.push_back(deconv_bwd_weights);
374 stream(stream::kind::lazy).submit(pipeline).wait();
376 auto weights_tr = memory({*con_weights_desc, *eng});
377 transpose_wei<data_t>(dd, weights->get(), weights_tr);
379 compare_data<data_t>(weights_tr, conv_weights);
382 auto ref_bias = memory({*dec_bias_desc, *eng});
383 compute_bias_bwd<data_t>(dd, dst->get(), ref_bias);
384 compare_data<data_t>(ref_bias, bias->get());
389 using deconvolution_test_float = deconvolution_test<float>;
391 TEST_P(deconvolution_test_float, TestDeconvolution)
395 #define EXPAND_FORMATS(src, weights, bias, dst) \
396 { mkldnn::memory::format::src, mkldnn::memory::format::weights, \
397 mkldnn::memory::format::bias, mkldnn::memory::format::dst }
399 #define ENGINE engine::kind::cpu
400 #define ALGORITHM mkldnn::deconvolution_direct
402 #define PARAMS(src, weights, bias, dst, ...) \
403 deconvolution_test_params { ENGINE, ALGORITHM, \
404 EXPAND_FORMATS(src, weights, bias, dst), {}, \
407 #define INST_TEST_CASE(str, ...) INSTANTIATE_TEST_CASE_P( \
408 str, deconvolution_test_float, ::testing::Values(__VA_ARGS__))
411 #define FMT_DATA_BLOCKED nChw8c
412 #define FMT_WEIGHTS_BLOCKED Ohwi8o
414 INST_TEST_CASE(SimpleSmall_NCHW,
415 PARAMS(nchw, oihw, x, nchw,
416 2, 1, 6, 4, 4, 4, 4, 4, 3, 3, 1, 1, 1, 1),
417 PARAMS(nchw, oihw, x, nchw,
418 2, 1, 6, 2, 2, 4, 4, 4, 3, 3, 0, 0, 1, 1),
419 PARAMS(nhwc, oihw, x, nhwc,
420 2, 1, 6, 2, 2, 4, 4, 4, 3, 3, 0, 0, 1, 1),
421 PARAMS(nhwc, hwio, x, nhwc,
422 2, 1, 6, 4, 4, 4, 4, 4, 3, 3, 1, 1, 1, 1),
423 PARAMS(nhwc, hwio, x, nhwc,
424 2, 1, 6, 2, 2, 4, 4, 4, 3, 3, 0, 0, 1, 1),
425 PARAMS(nhwc, goihw, x, nhwc,
426 2, 2, 6, 4, 4, 4, 4, 4, 3, 3, 0, 0, 1, 1),
427 PARAMS(nhwc, hwigo, x, nhwc,
428 2, 2, 6, 4, 4, 4, 4, 4, 3, 3, 1, 1, 1, 1)
432 INST_TEST_CASE(SimpleSmall_Blocked,
433 PARAMS(FMT_DATA_BLOCKED, FMT_WEIGHTS_BLOCKED, FMT_BIAS, FMT_DATA_BLOCKED,
434 2, 1, 32, 12, 12, 32, 13, 13, 3, 3, 0, 0, 1, 1),
435 PARAMS(FMT_DATA_BLOCKED, FMT_WEIGHTS_BLOCKED, FMT_BIAS, FMT_DATA_BLOCKED,
436 2, 1, 32, 4, 4, 32, 3, 3, 3, 3, 1, 1, 1, 1),
437 PARAMS(FMT_DATA_BLOCKED, FMT_WEIGHTS_BLOCKED, FMT_BIAS, FMT_DATA_BLOCKED,
438 2, 1, 32, 4, 4, 32, 4, 4, 3, 3, 0, 0, 1, 1),
439 PARAMS(FMT_DATA_BLOCKED, FMT_WEIGHTS_BLOCKED, FMT_BIAS, FMT_DATA_BLOCKED,
440 2, 1, 32, 2, 2, 32, 3, 3, 3, 3, 0, 0, 1, 1),
441 PARAMS(FMT_DATA_BLOCKED, FMT_WEIGHTS_BLOCKED, FMT_BIAS, FMT_DATA_BLOCKED,
442 2, 1, 32, 2, 2, 32, 2, 2, 3, 3, 1, 1, 1, 1),
443 PARAMS(FMT_DATA_BLOCKED, FMT_WEIGHTS_BLOCKED, FMT_BIAS, FMT_DATA_BLOCKED,
444 2, 1, 48, 13, 13, 32, 13, 13, 3, 3, 1, 1, 1, 1),
445 PARAMS(FMT_DATA_BLOCKED, FMT_WEIGHTS_BLOCKED, FMT_BIAS, FMT_DATA_BLOCKED,
446 2, 1, 48, 11, 11, 32, 13, 13, 3, 3, 0, 0, 1, 1)