mirror of
https://github.com/Tencent/ncnn.git
synced 2026-10-18 05:36:58 +08:00
bbaca40c35
* Reduce memory access in x86 deconvolution Col2im The SGEMM path fills the output with bias, then reads and updates each output value. When kernel tiles do not overlap, each value has one source. Write the source plus bias once, and fill only the output padding. Use this path for rectangular kernels with unit dilation and stride equal to the kernel size. Support packs of 1, 4, and 8. Split input rows between workers when there are fewer output channel groups than threads. Each worker writes separate output rows. Keep the existing path for other cases. Add 32 test cases to test_deconvolution. Test Plan --------- These commands passed in the local benchmark workspace: ```bash ctest --test-dir .tools/ncnn-col2im-build -R '^test_deconvolution$' --output-on-failure .venv-esp/bin/python results/oss/col2im_transfer_20260920/generalization/test_ncnn.py --parallel .venv-esp/bin/python results/oss/col2im_transfer_20260920/generalization/sanitize_ncnn.py ``` The complete forward test gave equal output bits for 236 cases and 1,433,578 values. The extracted loops passed ASan and UBSan for 300 cases. The sanitizer test uses a small Mat substitute, not a full sanitizer build. The local probe scripts are outside this repository. The final format check and git diff --check passed. The format changes do not change the tested operations. This change was prepared with help from an AI assistant.
609 lines
26 KiB
C++
609 lines
26 KiB
C++
// Copyright 2019 Tencent
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
#include "testutil.h"
|
|
|
|
#include "layer_type.h"
|
|
|
|
#include <limits.h>
|
|
|
|
static int test_deconvolution(int w, int h, int c, int outch, int kernel, int dilation, int stride, int pad, int bias, int output_pad_right, int output_pad_bottom, int output_w, int output_h, int activation_type = 0)
|
|
{
|
|
ncnn::Mat a = RandomMat(w, h, c);
|
|
|
|
if (output_w > 0 && output_h > 0 && pad != -233 && pad != -234)
|
|
{
|
|
pad = -233;
|
|
}
|
|
|
|
ncnn::ParamDict pd;
|
|
pd.set(0, outch);
|
|
pd.set(1, kernel);
|
|
pd.set(2, dilation);
|
|
pd.set(3, stride);
|
|
pd.set(4, pad);
|
|
pd.set(5, bias);
|
|
pd.set(6, outch * c * kernel * kernel);
|
|
|
|
ncnn::Mat activation_params(2);
|
|
activation_params[0] = activation_type == 2 ? 0.1f : -0.5f; // alpha
|
|
activation_params[1] = 0.25f; // beta
|
|
pd.set(9, activation_type);
|
|
pd.set(10, activation_params);
|
|
|
|
pd.set(18, output_pad_right);
|
|
pd.set(19, output_pad_bottom);
|
|
pd.set(20, output_w);
|
|
pd.set(21, output_h);
|
|
|
|
std::vector<ncnn::Mat> weights(2);
|
|
weights[0] = RandomMat(outch * c * kernel * kernel);
|
|
weights[1] = RandomMat(outch);
|
|
|
|
int ret = test_layer("Deconvolution", pd, weights, a, 0.001);
|
|
if (ret != 0)
|
|
{
|
|
fprintf(stderr, "test_deconvolution failed w=%d h=%d c=%d outch=%d kernel=%d dilation=%d stride=%d pad=%d bias=%d act=%d actparams=[%f,%f] output_pad_right=%d output_pad_bottom=%d output_w=%d output_h=%d\n", w, h, c, outch, kernel, dilation, stride, pad, bias, activation_type, activation_params[0], activation_params[1], output_pad_right, output_pad_bottom, output_w, output_h);
|
|
return ret;
|
|
}
|
|
|
|
{
|
|
ncnn::Option opt;
|
|
opt.num_threads = 1;
|
|
opt.use_packing_layout = true;
|
|
opt.use_fp16_packed = false;
|
|
opt.use_fp16_storage = false;
|
|
opt.use_fp16_arithmetic = false;
|
|
opt.use_bf16_packed = false;
|
|
opt.use_bf16_storage = false;
|
|
opt.use_sgemm_convolution = false;
|
|
opt.use_winograd_convolution = false;
|
|
|
|
ret = test_layer_opt("Deconvolution", pd, weights, opt, a, 0.001);
|
|
if (ret != 0)
|
|
{
|
|
fprintf(stderr, "test_deconvolution failed w=%d h=%d c=%d outch=%d kernel=%d dilation=%d stride=%d pad=%d bias=%d act=%d actparams=[%f,%f] output_pad_right=%d output_pad_bottom=%d output_w=%d output_h=%d\n", w, h, c, outch, kernel, dilation, stride, pad, bias, activation_type, activation_params[0], activation_params[1], output_pad_right, output_pad_bottom, output_w, output_h);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
{
|
|
ncnn::Option opt;
|
|
opt.num_threads = 1;
|
|
opt.use_packing_layout = true;
|
|
opt.use_fp16_packed = true;
|
|
opt.use_fp16_storage = true;
|
|
opt.use_fp16_arithmetic = true;
|
|
opt.use_bf16_packed = false;
|
|
opt.use_bf16_storage = false;
|
|
opt.use_sgemm_convolution = false;
|
|
opt.use_winograd_convolution = false;
|
|
|
|
ret = test_layer_opt("Deconvolution", pd, weights, opt, a, 0.001);
|
|
if (ret != 0)
|
|
{
|
|
fprintf(stderr, "test_deconvolution failed w=%d h=%d c=%d outch=%d kernel=%d dilation=%d stride=%d pad=%d bias=%d act=%d actparams=[%f,%f] output_pad_right=%d output_pad_bottom=%d output_w=%d output_h=%d\n", w, h, c, outch, kernel, dilation, stride, pad, bias, activation_type, activation_params[0], activation_params[1], output_pad_right, output_pad_bottom, output_w, output_h);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int test_deconvolution_0()
|
|
{
|
|
static const int kdsp[][4] = {
|
|
{1, 1, 1, 0}, {1, 1, 2, 0}, {2, 1, 1, 1}, {2, 1, 2, -233}, {3, 1, 1, 1}, {3, 1, 2, 1}, {3, 2, 1, 1}, {4, 1, 1, -233}, {4, 1, 2, -234}, {4, 2, 1, -234}, {5, 1, 1, 2}, {5, 1, 2, 2}, {5, 2, 2, 2}, {7, 1, 1, 3}, {7, 1, 2, 3}, {7, 2, 1, -233}
|
|
};
|
|
// scalar geometry covers every kernel, stride, dilation and padding boundary
|
|
for (size_t i = 0; i < sizeof(kdsp) / sizeof(kdsp[0]); i++)
|
|
{
|
|
const int* k = kdsp[i];
|
|
if (test_deconvolution(9, 7, 1, 1, k[0], k[1], k[2], k[3], 1, 0, 0, 0, 0))
|
|
return -1;
|
|
}
|
|
|
|
// large pack16 cases retain specialized kernels and the largest dilation
|
|
static const int large_kdsp[][4] = {
|
|
{1, 1, 1, 0}, {2, 1, 1, 1}, {3, 1, 1, 1}, {3, 1, 2, 1}, {4, 1, 1, -233}, {4, 1, 2, -234}, {7, 2, 1, -233}
|
|
};
|
|
for (size_t i = 0; i < sizeof(large_kdsp) / sizeof(large_kdsp[0]); i++)
|
|
{
|
|
const int* k = large_kdsp[i];
|
|
if (test_deconvolution(9, 7, 16, 16, k[0], k[1], k[2], k[3], 0, 0, 2, 7, 5))
|
|
return -1;
|
|
}
|
|
|
|
// pack4 geometry keeps generic, strided and dilated representatives
|
|
static const int packed_kdsp[][4] = {
|
|
{1, 1, 1, 0}, {3, 1, 1, 1}, {3, 1, 2, 1}, {7, 2, 1, -233}
|
|
};
|
|
for (size_t i = 0; i < sizeof(packed_kdsp) / sizeof(packed_kdsp[0]); i++)
|
|
{
|
|
const int* k = packed_kdsp[i];
|
|
if (test_deconvolution(7, 7, 12, 12, k[0], k[1], k[2], k[3], 1, 0, 1, 0, 0))
|
|
return -1;
|
|
}
|
|
|
|
// scalar tails retain each optimized 3x3 and 4x4 kernel in both directions
|
|
static const int specialized[][4] = {{3, 1, 1, 1}, {3, 1, 2, 1}, {4, 1, 1, -233}, {4, 1, 2, -234}};
|
|
// input channels, output channels, bias, output padding and explicit output size
|
|
static const int scalar_tails[][7] = {
|
|
{4, 13, 0, 1, 1, 7, 5}, {13, 4, 1, 1, 0, 0, 0}
|
|
};
|
|
for (size_t i = 0; i < sizeof(specialized) / sizeof(specialized[0]); i++)
|
|
{
|
|
for (size_t j = 0; j < sizeof(scalar_tails) / sizeof(scalar_tails[0]); j++)
|
|
{
|
|
const int* k = specialized[i];
|
|
const int* a = scalar_tails[j];
|
|
if (test_deconvolution(9, 7, a[0], a[1], k[0], k[1], k[2], k[3], a[2], a[3], a[4], a[5], a[6]) != 0)
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// mixed pack4 and pack8 directions use one generic kernel representative
|
|
static const int packing[][7] = {
|
|
{4, 8, 0, 0, 1, 0, 0}, {8, 4, 1, 0, 0, 7, 5}, {8, 13, 0, 2, 2, 0, 0}, {13, 8, 1, 2, 0, 0, 0}
|
|
};
|
|
for (size_t i = 0; i < sizeof(packing) / sizeof(packing[0]); i++)
|
|
{
|
|
const int* a = packing[i];
|
|
if (test_deconvolution(9, 7, a[0], a[1], 3, 1, 1, 1, a[2], a[3], a[4], a[5], a[6]) != 0)
|
|
return -1;
|
|
}
|
|
|
|
// generic packing keeps 1x1 and the largest dilated kernel
|
|
static const int generic[][4] = {{1, 1, 1, 0}, {7, 2, 1, -233}};
|
|
for (size_t i = 0; i < sizeof(generic) / sizeof(generic[0]); i++)
|
|
{
|
|
const int* k = generic[i];
|
|
if (test_deconvolution(9, 7, 4, 13, k[0], k[1], k[2], k[3], 0, 1, 1, 7, 5)
|
|
|| test_deconvolution(9, 7, 13, 8, k[0], k[1], k[2], k[3], 1, 2, 0, 0, 0))
|
|
return -1;
|
|
}
|
|
|
|
// every activation is tested directly for scalar and packed output
|
|
for (int activation = 0; activation < 5; activation++)
|
|
{
|
|
if (test_deconvolution(5, 4, 7, 7, 3, 1, 1, 1, 1, 0, 0, 0, 0, activation)
|
|
|| test_deconvolution(5, 4, 8, 16, 3, 1, 1, 1, 0, 0, 0, 0, 0, activation))
|
|
return -1;
|
|
}
|
|
|
|
// explicit width-only and height-only requests cover independent crop boundaries
|
|
if (test_deconvolution(4, 5, 12, 11, 3, 1, 1, 0, 0, 0, 1, 1, 0)
|
|
|| test_deconvolution(4, 5, 12, 11, 3, 1, 1, 0, 0, 0, 1, 0, 1))
|
|
return -1;
|
|
|
|
// tier coverage for small outch and various elempack
|
|
return 0
|
|
|| test_deconvolution(5, 4, 7, 3, 1, 1, 1, 0, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution(6, 7, 2, 3, 2, 1, 1, 1, 1, 0, 0, 0, 0)
|
|
|| test_deconvolution(5, 3, 1, 3, 1, 1, 1, 0, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution(8, 6, 8, 2, 3, 2, 1, 1, 1, 0, 0, 0, 0)
|
|
|| test_deconvolution(8, 4, 8, 15, 1, 1, 2, 0, 0, 0, 1, 0, 0)
|
|
// kernel transform num_input < 4 paths
|
|
|| test_deconvolution(4, 5, 1, 8, 3, 1, 1, 1, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution(5, 4, 2, 7, 1, 1, 1, 0, 1, 0, 0, 0, 0)
|
|
|| test_deconvolution(5, 4, 2, 8, 3, 1, 1, 1, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution(4, 5, 2, 1, 3, 1, 1, 1, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution(5, 4, 1, 5, 1, 1, 1, 0, 0, 0, 0, 0, 0)
|
|
// elempack==1 large inch for inner tier cascading
|
|
|| test_deconvolution(3, 3, 14, 2, 3, 1, 1, 1, 1, 0, 0, 0, 0)
|
|
|| test_deconvolution(3, 3, 14, 1, 3, 1, 1, 1, 0, 0, 0, 0, 0)
|
|
// avx512 tier coverage
|
|
|| test_deconvolution(5, 4, 1, 16, 1, 1, 1, 0, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution(5, 4, 2, 16, 3, 1, 1, 1, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution(5, 4, 8, 16, 3, 1, 2, 1, 1, 0, 0, 0, 0)
|
|
|| test_deconvolution(4, 3, 5, 16, 1, 1, 1, 0, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution(5, 4, 24, 8, 3, 1, 1, 1, 1, 0, 0, 0, 0)
|
|
|| test_deconvolution(5, 4, 20, 8, 3, 1, 1, 1, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution(4, 5, 17, 8, 1, 1, 1, 0, 1, 0, 0, 0, 0)
|
|
|| test_deconvolution(5, 4, 7, 5, 3, 1, 1, 1, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution(4, 3, 24, 5, 3, 1, 1, 1, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution(5, 4, 20, 5, 3, 1, 1, 1, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution(4, 3, 17, 5, 3, 1, 1, 1, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution(4, 3, 24, 2, 3, 1, 1, 1, 1, 0, 0, 0, 0)
|
|
|| test_deconvolution(5, 4, 17, 2, 3, 1, 1, 1, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution(5, 4, 20, 2, 3, 1, 2, 1, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution(4, 3, 7, 1, 3, 1, 1, 1, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution(5, 4, 16, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution(4, 3, 24, 1, 3, 1, 1, 1, 1, 0, 0, 0, 0)
|
|
|| test_deconvolution(5, 4, 20, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution(4, 3, 17, 1, 3, 1, 1, 1, 1, 0, 0, 0, 0)
|
|
|| test_deconvolution(7, 5, 24, 32, 4, 2, 2, 2, 1, 0, 0, 0, 0)
|
|
|| test_deconvolution(7, 5, 32, 24, 4, 2, 2, 2, 1, 0, 0, 0, 0)
|
|
|| test_deconvolution(7, 5, 28, 32, 4, 2, 2, 2, 1, 0, 0, 0, 0)
|
|
|| test_deconvolution(7, 5, 32, 28, 4, 2, 2, 2, 1, 0, 0, 0, 0)
|
|
|| test_deconvolution(7, 5, 26, 32, 4, 2, 2, 2, 1, 0, 0, 0, 0)
|
|
|| test_deconvolution(7, 5, 32, 26, 4, 2, 2, 2, 1, 0, 0, 0, 0);
|
|
}
|
|
|
|
static int test_deconvolution_dynamic(int w, int h, int c, int outch, int kernel, int dilation, int stride, int pad, int bias, int output_pad_right, int output_pad_bottom, int output_w, int output_h)
|
|
{
|
|
ncnn::Mat a = RandomMat(w, h, c);
|
|
|
|
if (output_w > 0 && output_h > 0 && pad != -233 && pad != -234)
|
|
{
|
|
pad = -233;
|
|
}
|
|
|
|
ncnn::ParamDict pd;
|
|
pd.set(0, 0);
|
|
pd.set(1, 0);
|
|
pd.set(2, dilation);
|
|
pd.set(3, stride);
|
|
pd.set(4, pad);
|
|
pd.set(5, bias);
|
|
pd.set(6, 0);
|
|
pd.set(28, 1); // dynamic weight
|
|
|
|
int activation_type = RAND() % 7; // 0 1 2 3 4 5 6
|
|
ncnn::Mat activation_params(2);
|
|
activation_params[0] = (activation_type == 6) ? RandomFloat(0, 1) : RandomFloat(-1, 0); // alpha
|
|
activation_params[1] = RandomFloat(0, 1); // beta
|
|
pd.set(9, activation_type);
|
|
pd.set(10, activation_params);
|
|
|
|
pd.set(18, output_pad_right);
|
|
pd.set(19, output_pad_bottom);
|
|
pd.set(20, output_w);
|
|
pd.set(21, output_h);
|
|
|
|
std::vector<ncnn::Mat> as(bias ? 3 : 2);
|
|
as[0] = a;
|
|
as[1] = RandomMat(kernel, kernel, outch, c);
|
|
if (bias)
|
|
as[2] = RandomMat(outch);
|
|
|
|
std::vector<ncnn::Mat> weights(0);
|
|
|
|
int ret = test_layer("Deconvolution", pd, weights, as);
|
|
if (ret != 0)
|
|
{
|
|
fprintf(stderr, "test_deconvolution_dynamic failed w=%d h=%d c=%d outch=%d kernel=%d dilation=%d stride=%d pad=%d bias=%d act=%d actparams=[%f,%f] output_pad_right=%d output_pad_bottom=%d output_w=%d output_h=%d\n", w, h, c, outch, kernel, dilation, stride, pad, bias, activation_type, activation_params[0], activation_params[1], output_pad_right, output_pad_bottom, output_w, output_h);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int test_deconvolution_1()
|
|
{
|
|
static const int kdsp[7][4] = {
|
|
{1, 1, 1, 0},
|
|
{1, 1, 2, 0},
|
|
{2, 1, 1, 1},
|
|
{2, 1, 2, -233},
|
|
{3, 1, 1, 1},
|
|
{3, 1, 2, 1},
|
|
{3, 2, 1, -234},
|
|
};
|
|
|
|
for (int i = 0; i < 7; i++)
|
|
{
|
|
const int k = kdsp[i][0];
|
|
const int d = kdsp[i][1];
|
|
const int s = kdsp[i][2];
|
|
const int p = kdsp[i][3];
|
|
|
|
int ret = 0
|
|
|| test_deconvolution_dynamic(9, 7, 1, 1, k, d, s, p, 1, 0, 0, 0, 0)
|
|
|| test_deconvolution_dynamic(9, 7, 4, 13, k, d, s, p, 0, 1, 1, 7, 5)
|
|
|| test_deconvolution_dynamic(9, 7, 13, 4, k, d, s, p, 1, 1, 0, 0, 0)
|
|
|| test_deconvolution_dynamic(9, 7, 4, 8, k, d, s, p, 0, 0, 1, 0, 0)
|
|
|| test_deconvolution_dynamic(9, 7, 8, 4, k, d, s, p, 1, 0, 0, 7, 5)
|
|
|| test_deconvolution_dynamic(7, 7, 12, 12, k, d, s, p, 1, 0, 1, 0, 0)
|
|
|| test_deconvolution_dynamic(4, 5, 12, 11, k, d, s, p, 0, 0, 1, 1, 0)
|
|
|| test_deconvolution_dynamic(9, 7, 8, 13, k, d, s, p, 0, 2, 2, 0, 0)
|
|
|| test_deconvolution_dynamic(9, 7, 13, 8, k, d, s, p, 1, 2, 0, 0, 0)
|
|
|| test_deconvolution_dynamic(9, 7, 16, 16, k, d, s, p, 0, 0, 2, 7, 5);
|
|
|
|
if (ret != 0)
|
|
return -1;
|
|
}
|
|
|
|
// tier coverage for small outch and various elempack
|
|
return 0
|
|
|| test_deconvolution_dynamic(5, 4, 7, 3, 1, 1, 1, 0, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution_dynamic(6, 7, 2, 3, 2, 1, 1, 1, 1, 0, 0, 0, 0)
|
|
|| test_deconvolution_dynamic(5, 3, 1, 3, 1, 1, 1, 0, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution_dynamic(8, 6, 8, 2, 3, 2, 1, 1, 1, 0, 0, 0, 0)
|
|
|| test_deconvolution_dynamic(8, 4, 8, 15, 1, 1, 2, 0, 0, 0, 1, 0, 0)
|
|
// kernel transform num_input < 4 paths
|
|
|| test_deconvolution_dynamic(4, 5, 1, 8, 3, 1, 1, 1, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution_dynamic(5, 4, 2, 7, 1, 1, 1, 0, 1, 0, 0, 0, 0)
|
|
|| test_deconvolution_dynamic(5, 4, 2, 8, 3, 1, 1, 1, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution_dynamic(4, 5, 2, 1, 3, 1, 1, 1, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution_dynamic(5, 4, 1, 5, 1, 1, 1, 0, 0, 0, 0, 0, 0)
|
|
// elempack==1 large inch for inner tier cascading
|
|
|| test_deconvolution_dynamic(3, 3, 14, 2, 3, 1, 1, 1, 1, 0, 0, 0, 0)
|
|
|| test_deconvolution_dynamic(3, 3, 14, 1, 3, 1, 1, 1, 0, 0, 0, 0, 0)
|
|
// avx512 tier coverage
|
|
|| test_deconvolution_dynamic(5, 4, 1, 16, 1, 1, 1, 0, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution_dynamic(5, 4, 2, 16, 3, 1, 1, 1, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution_dynamic(5, 4, 8, 16, 3, 1, 2, 1, 1, 0, 0, 0, 0)
|
|
|| test_deconvolution_dynamic(4, 3, 5, 16, 1, 1, 1, 0, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution_dynamic(5, 4, 24, 8, 3, 1, 1, 1, 1, 0, 0, 0, 0)
|
|
|| test_deconvolution_dynamic(5, 4, 20, 8, 3, 1, 1, 1, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution_dynamic(4, 5, 17, 8, 1, 1, 1, 0, 1, 0, 0, 0, 0)
|
|
|| test_deconvolution_dynamic(5, 4, 7, 5, 3, 1, 1, 1, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution_dynamic(4, 3, 24, 5, 3, 1, 1, 1, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution_dynamic(5, 4, 20, 5, 3, 1, 1, 1, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution_dynamic(4, 3, 17, 5, 3, 1, 1, 1, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution_dynamic(4, 3, 24, 2, 3, 1, 1, 1, 1, 0, 0, 0, 0)
|
|
|| test_deconvolution_dynamic(5, 4, 17, 2, 3, 1, 1, 1, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution_dynamic(5, 4, 20, 2, 3, 1, 2, 1, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution_dynamic(4, 3, 7, 1, 3, 1, 1, 1, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution_dynamic(5, 4, 16, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution_dynamic(4, 3, 24, 1, 3, 1, 1, 1, 1, 0, 0, 0, 0)
|
|
|| test_deconvolution_dynamic(5, 4, 20, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0)
|
|
|| test_deconvolution_dynamic(4, 3, 17, 1, 3, 1, 1, 1, 1, 0, 0, 0, 0);
|
|
}
|
|
|
|
static int test_deconvolution_activation_params_text()
|
|
{
|
|
#if NCNN_STRING
|
|
const char* params[] = {"0=1 1=1 6=1 9=3 -23310=2,-1,2", "0=1 1=1 6=1 9=3 -23310=2,-1.0,2.0"};
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
TestParamDict pd;
|
|
if (pd.load_param(params[i]) != 0 || pd.type(10) != 5 + i)
|
|
return -1;
|
|
|
|
std::vector<ncnn::Mat> weights(1);
|
|
weights[0].create(1);
|
|
weights[0][0] = 1.f;
|
|
|
|
ncnn::Mat a(2, 1, 1);
|
|
a[0] = -3.f;
|
|
a[1] = 3.f;
|
|
ncnn::Mat reference(2, 1, 1);
|
|
reference[0] = -1.f;
|
|
reference[1] = 2.f;
|
|
ncnn::Mat b;
|
|
int ret = test_layer_naive(ncnn::LayerType::Deconvolution, pd, weights, a, b, 0);
|
|
if (ret == 0)
|
|
ret = CompareMat(reference, b, 0.f);
|
|
if (ret != 0)
|
|
{
|
|
fprintf(stderr, "test_deconvolution_activation_params_text failed params=%s ret=%d\n", params[i], ret);
|
|
return ret;
|
|
}
|
|
|
|
const ncnn::Mat original = pd.get(10, ncnn::Mat());
|
|
const int* p = original;
|
|
if (i == 0 ? (p[0] != -1 || p[1] != 2) : (original[0] != -1.f || original[1] != 2.f))
|
|
{
|
|
fprintf(stderr, "test_deconvolution_activation_params_text modified params=%s\n", params[i]);
|
|
return -1;
|
|
}
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
#if NCNN_VALIDATION
|
|
static int test_deconvolution_load_param_activation(const ncnn::ParamDict& base, int activation_type, const ncnn::Mat& activation_params, int expected_ret)
|
|
{
|
|
ncnn::ParamDict pd = base;
|
|
pd.set(9, activation_type);
|
|
pd.set(10, activation_params);
|
|
|
|
return test_layer_param(ncnn::LayerType::Deconvolution, pd, expected_ret);
|
|
}
|
|
|
|
static int test_deconvolution_load_param()
|
|
{
|
|
ncnn::ParamDict base;
|
|
base.set(0, 8);
|
|
base.set(1, 3);
|
|
base.set(6, 216);
|
|
if (test_layer_param(ncnn::LayerType::Deconvolution, base, 0) != 0)
|
|
return -1;
|
|
|
|
ncnn::Mat params(2);
|
|
params[0] = 0.1f;
|
|
params[1] = 0.5f;
|
|
|
|
int ret = 0
|
|
|| test_deconvolution_load_param_activation(base, 0, ncnn::Mat(), 0)
|
|
|| test_deconvolution_load_param_activation(base, 0, ncnn::Mat(0), 0)
|
|
|| test_deconvolution_load_param_activation(base, 0, params, 0)
|
|
|| test_deconvolution_load_param_activation(base, 0, params.range(0, 1), 0)
|
|
|| test_deconvolution_load_param_activation(base, 1, ncnn::Mat(), 0)
|
|
|| test_deconvolution_load_param_activation(base, 1, ncnn::Mat(0), 0)
|
|
|| test_deconvolution_load_param_activation(base, 1, params, 0)
|
|
|| test_deconvolution_load_param_activation(base, 1, params.range(0, 1), 0)
|
|
|| test_deconvolution_load_param_activation(base, 2, ncnn::Mat(), -1)
|
|
|| test_deconvolution_load_param_activation(base, 2, ncnn::Mat(0), -1)
|
|
|| test_deconvolution_load_param_activation(base, 2, params, 0)
|
|
|| test_deconvolution_load_param_activation(base, 2, params.range(0, 1), 0)
|
|
|| test_deconvolution_load_param_activation(base, 3, ncnn::Mat(), -1)
|
|
|| test_deconvolution_load_param_activation(base, 3, ncnn::Mat(0), -1)
|
|
|| test_deconvolution_load_param_activation(base, 3, params, 0)
|
|
|| test_deconvolution_load_param_activation(base, 3, params.range(0, 1), -1)
|
|
|| test_deconvolution_load_param_activation(base, 4, ncnn::Mat(), 0)
|
|
|| test_deconvolution_load_param_activation(base, 4, ncnn::Mat(0), 0)
|
|
|| test_deconvolution_load_param_activation(base, 4, params, 0)
|
|
|| test_deconvolution_load_param_activation(base, 4, params.range(0, 1), 0)
|
|
|| test_deconvolution_load_param_activation(base, 5, ncnn::Mat(), 0)
|
|
|| test_deconvolution_load_param_activation(base, 5, ncnn::Mat(0), 0)
|
|
|| test_deconvolution_load_param_activation(base, 5, params, 0)
|
|
|| test_deconvolution_load_param_activation(base, 5, params.range(0, 1), 0)
|
|
|| test_deconvolution_load_param_activation(base, 6, ncnn::Mat(), -1)
|
|
|| test_deconvolution_load_param_activation(base, 6, ncnn::Mat(0), -1)
|
|
|| test_deconvolution_load_param_activation(base, 6, params, 0)
|
|
|| test_deconvolution_load_param_activation(base, 6, params.range(0, 1), -1)
|
|
|| test_deconvolution_load_param_activation(base, 7, ncnn::Mat(), -1);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
if (test_layer_param(ncnn::LayerType::Deconvolution, base, 10, 1, -1)
|
|
|| test_layer_param(ncnn::LayerType::Deconvolution, base, 10, 1.f, -1))
|
|
return -1;
|
|
|
|
ncnn::Mat missing_data(0);
|
|
missing_data.w = 1;
|
|
|
|
ncnn::Mat negative_length(0);
|
|
negative_length.w = -1;
|
|
|
|
const ncnn::Mat bad[] = {ncnn::Mat(2, (size_t)1u), ncnn::Mat(2, (size_t)2u), ncnn::Mat(2, 2), ncnn::Mat(2, (size_t)16u, 4), missing_data, negative_length};
|
|
for (size_t i = 0; i < sizeof(bad) / sizeof(bad[0]); i++)
|
|
{
|
|
if (test_layer_param(ncnn::LayerType::Deconvolution, base, 10, bad[i], -1) != 0)
|
|
return -1;
|
|
}
|
|
|
|
ret = 0
|
|
|| test_layer_param(ncnn::LayerType::Deconvolution, base, 0, 0, -1)
|
|
|| test_layer_param(ncnn::LayerType::Deconvolution, base, 1, 0, -1)
|
|
|| test_layer_param(ncnn::LayerType::Deconvolution, base, 2, 0, -1)
|
|
|| test_layer_param(ncnn::LayerType::Deconvolution, base, 3, 0, -1)
|
|
|| test_layer_param(ncnn::LayerType::Deconvolution, base, 1, INT_MAX, -1)
|
|
|| test_layer_param(ncnn::LayerType::Deconvolution, base, 6, 217, -1);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int test_deconvolution_load_param_text()
|
|
{
|
|
#if NCNN_STRING
|
|
const char* params[] = {"0=1 1=1 6=1 9=3 -23310=2,-1,2", "0=1 1=1 6=1 9=3 -23310=2,-1.0,2.0"};
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
TestParamDict pd;
|
|
if (pd.load_param(params[i]) != 0 || pd.type(10) != 5 + i)
|
|
return -1;
|
|
|
|
if (test_layer_param(ncnn::LayerType::Deconvolution, pd, 0) != 0)
|
|
return -1;
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
#endif // NCNN_VALIDATION
|
|
|
|
static int test_deconvolution_nonoverlap_case(int kw, int kh, int outch, int h, int bias, int output_pad_right, int output_pad_bottom, int pad = 0, int activation_type = 0)
|
|
{
|
|
ncnn::Mat a = RandomMat(7, h, 8);
|
|
ncnn::ParamDict pd;
|
|
pd.set(0, outch);
|
|
pd.set(1, kw);
|
|
pd.set(11, kh);
|
|
pd.set(3, kw);
|
|
pd.set(13, kh);
|
|
pd.set(4, pad);
|
|
pd.set(5, bias);
|
|
pd.set(6, outch * 8 * kw * kh);
|
|
pd.set(18, output_pad_right);
|
|
pd.set(19, output_pad_bottom);
|
|
pd.set(9, activation_type);
|
|
|
|
ncnn::Mat activation_params(2);
|
|
activation_params[0] = activation_type == 2 ? 0.1f : -0.5f;
|
|
activation_params[1] = 0.25f;
|
|
pd.set(10, activation_params);
|
|
|
|
std::vector<ncnn::Mat> weights(2);
|
|
weights[0] = RandomMat(outch * 8 * kw * kh);
|
|
weights[1] = RandomMat(outch);
|
|
for (int threaded = 0; threaded < 2; threaded++)
|
|
{
|
|
const int flag = threaded ? TEST_LAYER_ENABLE_THREADING : 0;
|
|
for (int packed = 0; packed < 2; packed++)
|
|
{
|
|
ncnn::Option opt;
|
|
opt.num_threads = 1;
|
|
opt.use_packing_layout = packed;
|
|
opt.use_sgemm_convolution = true;
|
|
opt.use_fp16_packed = false;
|
|
opt.use_fp16_storage = false;
|
|
opt.use_fp16_arithmetic = false;
|
|
opt.use_bf16_packed = false;
|
|
opt.use_bf16_storage = false;
|
|
if (test_layer_opt("Deconvolution", pd, weights, opt, a, 0.001f, flag) != 0)
|
|
{
|
|
fprintf(stderr, "test_deconvolution_nonoverlap failed kw=%d kh=%d outch=%d h=%d bias=%d output_pad_right=%d output_pad_bottom=%d pad=%d activation=%d threaded=%d packed=%d\n", kw, kh, outch, h, bias, output_pad_right, output_pad_bottom, pad, activation_type, threaded, packed);
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int test_deconvolution_nonoverlap()
|
|
{
|
|
const int shapes[][4] = {
|
|
{1, 1, 1, 1},
|
|
{2, 2, 8, 3},
|
|
{2, 3, 4, 1},
|
|
{3, 2, 8, 3},
|
|
{3, 3, 16, 5},
|
|
{4, 4, 16, 7},
|
|
{1, 5, 3, 9},
|
|
{5, 1, 4, 3}
|
|
};
|
|
for (size_t i = 0; i < sizeof(shapes) / sizeof(shapes[0]); i++)
|
|
{
|
|
const int* s = shapes[i];
|
|
if (test_deconvolution_nonoverlap_case(s[0], s[1], s[2], s[3], i % 2, i % 2, (i / 2) % 2) != 0)
|
|
return -1;
|
|
}
|
|
|
|
// one output group and odd height exercise row splitting with two or more threads
|
|
// retain right and bottom padding, then combine crop with activation
|
|
return 0
|
|
|| test_deconvolution_nonoverlap_case(2, 3, 1, 5, 0, 1, 1)
|
|
|| test_deconvolution_nonoverlap_case(2, 3, 4, 5, 1, 1, 1)
|
|
|| test_deconvolution_nonoverlap_case(2, 3, 8, 5, 0, 1, 1)
|
|
|| test_deconvolution_nonoverlap_case(3, 2, 1, 5, 1, 1, 1, 1, 1)
|
|
|| test_deconvolution_nonoverlap_case(3, 2, 4, 5, 0, 1, 1, 1, 3)
|
|
|| test_deconvolution_nonoverlap_case(3, 2, 8, 5, 1, 1, 1, 1, 2);
|
|
}
|
|
|
|
static int test_deconvolution_activation_boundaries()
|
|
{
|
|
// generic kernels exercise activation fusion across input and output packing
|
|
for (int activation = 0; activation < 5; activation++)
|
|
{
|
|
if (test_deconvolution(3, 3, 3, 3, 2, 1, 1, 0, 1, 0, 0, 0, 0, activation)
|
|
|| test_deconvolution(3, 3, 4, 3, 2, 1, 1, 0, 1, 0, 0, 0, 0, activation)
|
|
|| test_deconvolution(3, 3, 3, 8, 2, 1, 1, 0, 1, 0, 0, 0, 0, activation)
|
|
|| test_deconvolution(3, 3, 8, 3, 2, 1, 1, 0, 1, 0, 0, 0, 0, activation))
|
|
return -1;
|
|
}
|
|
|
|
// specialized scalar kernels apply activation after the deconvolution
|
|
return 0
|
|
|| test_deconvolution(3, 3, 1, 1, 3, 1, 2, 0, 0, 0, 0, 0, 0, 1)
|
|
|| test_deconvolution(3, 3, 1, 1, 4, 1, 1, 0, 0, 0, 0, 0, 0, 1)
|
|
|| test_deconvolution(3, 3, 1, 1, 4, 1, 2, 0, 0, 0, 0, 0, 0, 1);
|
|
}
|
|
|
|
static int test_deconvolution_stride_packing_boundary()
|
|
{
|
|
// scalar input remainder covers both stride phases in packed output
|
|
return test_deconvolution(9, 7, 13, 8, 3, 1, 2, 1, 1, 2, 0, 0, 0);
|
|
}
|
|
|
|
int main()
|
|
{
|
|
SRAND(7767517);
|
|
|
|
return 0
|
|
|| test_deconvolution_0()
|
|
|| test_deconvolution_1()
|
|
|| test_deconvolution_nonoverlap()
|
|
|| test_deconvolution_activation_params_text()
|
|
#if NCNN_VALIDATION
|
|
|| test_deconvolution_load_param()
|
|
|| test_deconvolution_load_param_text()
|
|
#endif // NCNN_VALIDATION
|
|
|| test_deconvolution_activation_boundaries()
|
|
|| test_deconvolution_stride_packing_boundary();
|
|
}
|