Files
ncnn/tests/test_innerproduct.cpp
nihui f6f734f44d vulkan int8 kernels (#6751)
* vulkan: add int8 kernels for core layers
Implement Vulkan int8 paths for convolution, convolutiondepthwise, innerproduct, and gemm with scalar fallback and integer dot product shader branches. Add goal.md and enable existing int8 unit tests for Vulkan coverage.
* enable int8 gpu benchmark
2026-06-17 17:39:49 +08:00

353 lines
13 KiB
C++

// Copyright 2020 Tencent
// SPDX-License-Identifier: BSD-3-Clause
#include "testutil.h"
static int test_innerproduct(const ncnn::Mat& a, int outch, int bias)
{
ncnn::ParamDict pd;
pd.set(0, outch); // num_output
pd.set(1, bias); // bias_term
pd.set(2, outch * a.w * a.h * a.d * a.c);
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);
std::vector<ncnn::Mat> weights(bias ? 2 : 1);
weights[0] = RandomMat(outch * a.w * a.h * a.d * a.c);
if (bias)
weights[1] = RandomMat(outch);
int ret = test_layer("InnerProduct", pd, weights, a);
if (ret != 0)
{
fprintf(stderr, "test_innerproduct failed a.dims=%d a=(%d %d %d %d) outch=%d bias=%d act=%d actparams=[%f,%f]\n", a.dims, a.w, a.h, a.d, a.c, outch, bias, activation_type, activation_params[0], activation_params[1]);
}
return ret;
}
static int test_innerproduct_0()
{
return 0
|| test_innerproduct(RandomMat(1, 3, 1), 1, 1)
|| test_innerproduct(RandomMat(3, 2, 2), 2, 0)
|| test_innerproduct(RandomMat(9, 3, 8), 7, 1)
|| test_innerproduct(RandomMat(2, 2, 8), 8, 0)
|| test_innerproduct(RandomMat(4, 3, 15), 8, 1)
|| test_innerproduct(RandomMat(6, 2, 16), 16, 0)
|| test_innerproduct(RandomMat(6, 2, 16), 7, 1)
|| test_innerproduct(RandomMat(6, 2, 5), 16, 1);
}
static int test_innerproduct_1()
{
return 0
|| test_innerproduct(RandomMat(1, 1), 1, 1)
|| test_innerproduct(RandomMat(3, 2), 2, 0)
|| test_innerproduct(RandomMat(9, 8), 7, 1)
|| test_innerproduct(RandomMat(2, 8), 8, 0)
|| test_innerproduct(RandomMat(4, 15), 8, 1)
|| test_innerproduct(RandomMat(6, 16), 16, 0)
|| test_innerproduct(RandomMat(6, 16), 7, 1)
|| test_innerproduct(RandomMat(6, 5), 16, 1);
}
static int test_innerproduct_2()
{
return 0
|| test_innerproduct(RandomMat(1), 1, 1)
|| test_innerproduct(RandomMat(2), 2, 0)
|| test_innerproduct(RandomMat(8), 7, 1)
|| test_innerproduct(RandomMat(8), 8, 0)
|| test_innerproduct(RandomMat(15), 8, 1)
|| test_innerproduct(RandomMat(15), 15, 1)
|| test_innerproduct(RandomMat(16), 16, 0)
|| test_innerproduct(RandomMat(16), 7, 1)
|| test_innerproduct(RandomMat(5), 16, 0)
|| test_innerproduct(RandomMat(32), 16, 1)
|| test_innerproduct(RandomMat(12), 16, 0)
|| test_innerproduct(RandomMat(16), 12, 1)
|| test_innerproduct(RandomMat(24), 32, 1);
}
static int test_innerproduct_3()
{
return 0
|| test_innerproduct(RandomMat(2, 2, 3, 1), 8, 1)
|| test_innerproduct(RandomMat(5, 3, 2, 3), 7, 0)
|| test_innerproduct(RandomMat(3, 2, 2, 5), 8, 1)
|| test_innerproduct(RandomMat(4, 3, 3, 4), 15, 1)
|| test_innerproduct(RandomMat(2, 2, 3, 16), 16, 0);
}
#if NCNN_INT8
static int test_innerproduct_int8(const ncnn::Mat& a, int outch, int bias, bool input_int8 = false, bool weight_int8 = false)
{
ncnn::ParamDict pd;
pd.set(0, outch); // num_output
pd.set(1, bias); // bias_term
pd.set(2, outch * a.w * a.h * a.d * a.c);
pd.set(8, 1); // int8_scale_term
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);
std::vector<ncnn::Mat> weights(bias ? 4 : 3);
const int k = a.w * a.h * a.d * a.c;
weights[0] = weight_int8 ? RandomS8Mat(outch * k) : RandomMat(outch * k);
ncnn::Mat weight_scales = weight_int8 ? RandomMat(outch, 10.f, 20.f) : scales_mat(weights[0], outch, k, k);
ncnn::Mat input_scales = scales_mat(a, 1, k, k);
ncnn::Mat a_int8 = a;
if (input_int8)
{
ncnn::Option opt;
opt.num_threads = 1;
opt.use_packing_layout = false;
ncnn::quantize_to_int8(a, a_int8, input_scales, opt);
}
if (bias)
{
weights[1] = RandomMat(outch);
weights[2] = weight_scales;
weights[3] = input_scales;
}
else
{
weights[1] = weight_scales;
weights[2] = input_scales;
}
int flag = input_int8 ? TEST_LAYER_DISABLE_AUTO_INPUT_CASTING : 0;
int ret = 0;
if (input_int8)
{
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;
ret = test_layer_opt("InnerProduct", pd, weights, opt, a_int8, 0.001f, flag);
}
else
{
ret = test_layer("InnerProduct", pd, weights, a_int8, 0.001f, flag);
}
if (ret != 0)
{
fprintf(stderr, "test_innerproduct_int8 failed a.dims=%d a=(%d %d %d %d) outch=%d bias=%d input_int8=%d weight_int8=%d act=%d actparams=[%f,%f]\n", a.dims, a.w, a.h, a.d, a.c, outch, bias, input_int8, weight_int8, activation_type, activation_params[0], activation_params[1]);
}
return ret;
}
static int test_innerproduct_4()
{
return 0
|| test_innerproduct_int8(RandomMat(1, 3, 1), 1, 1)
|| test_innerproduct_int8(RandomMat(3, 2, 2), 2, 1)
|| test_innerproduct_int8(RandomMat(5, 3, 3), 3, 1)
|| test_innerproduct_int8(RandomMat(7, 2, 3), 12, 1)
|| test_innerproduct_int8(RandomMat(9, 3, 4), 4, 1)
|| test_innerproduct_int8(RandomMat(2, 2, 7), 7, 1)
|| test_innerproduct_int8(RandomMat(4, 3, 8), 3, 1)
|| test_innerproduct_int8(RandomMat(6, 2, 8), 8, 1)
|| test_innerproduct_int8(RandomMat(8, 3, 15), 15, 1)
|| test_innerproduct_int8(RandomMat(7, 2, 16), 4, 1)
|| test_innerproduct_int8(RandomMat(6, 3, 16), 16, 1)
|| test_innerproduct_int8(RandomMat(16), 16, 1, true)
|| test_innerproduct_int8(RandomMat(32), 16, 1, false, true)
|| test_innerproduct_int8(RandomMat(16), 12, 1, true, true)
|| test_innerproduct_int8(RandomMat(2, 2, 1), 7, 1, true)
|| test_innerproduct_int8(RandomMat(2, 2, 2), 7, 1, true)
|| test_innerproduct_int8(RandomMat(2, 2, 3), 7, 1, true)
|| test_innerproduct_int8(RandomMat(2, 2, 4), 8, 1, true);
}
#endif // NCNN_INT8
static int test_innerproduct_gemm(const ncnn::Mat& a, int outch, int bias)
{
ncnn::ParamDict pd;
pd.set(0, outch);
pd.set(1, bias);
pd.set(2, outch * a.w);
int activation_type = RAND() % 7;
ncnn::Mat activation_params(2);
activation_params[0] = (activation_type == 6) ? RandomFloat(0, 1) : RandomFloat(-1, 0); // alpha
activation_params[1] = RandomFloat(0, 1);
pd.set(9, activation_type);
pd.set(10, activation_params);
std::vector<ncnn::Mat> weights(bias ? 2 : 1);
weights[0] = RandomMat(outch * a.w);
if (bias)
weights[1] = RandomMat(outch);
int ret = test_layer("InnerProduct", pd, weights, a);
if (ret != 0)
{
fprintf(stderr, "test_innerproduct_gemm failed a.dims=%d a=(%d %d %d) outch=%d bias=%d act=%d actparams=[%f,%f]\n", a.dims, a.w, a.h, a.c, outch, bias, activation_type, activation_params[0], activation_params[1]);
}
return ret;
}
static int test_innerproduct_5()
{
return 0
|| test_innerproduct_gemm(RandomMat(1, 1), 1, 1)
|| test_innerproduct_gemm(RandomMat(48, 1), 11, 1)
|| test_innerproduct_gemm(RandomMat(1, 5), 1, 1)
|| test_innerproduct_gemm(RandomMat(3, 2), 2, 0)
|| test_innerproduct_gemm(RandomMat(9, 8), 7, 1)
|| test_innerproduct_gemm(RandomMat(2, 8), 8, 0)
|| test_innerproduct_gemm(RandomMat(13, 20), 8, 1)
|| test_innerproduct_gemm(RandomMat(16, 20), 16, 0)
|| test_innerproduct_gemm(RandomMat(11, 24), 8, 0)
|| test_innerproduct_gemm(RandomMat(13, 24), 12, 1)
|| test_innerproduct_gemm(RandomMat(15, 20), 20, 1)
|| test_innerproduct_gemm(RandomMat(16, 20), 11, 1)
|| test_innerproduct_gemm(RandomMat(19, 16), 16, 1)
|| test_innerproduct_gemm(RandomMat(15, 15), 15, 1)
|| test_innerproduct_gemm(RandomMat(14, 15), 8, 1)
|| test_innerproduct_gemm(RandomMat(17, 15), 12, 1)
|| test_innerproduct_gemm(RandomMat(12, 16), 7, 1)
|| test_innerproduct_gemm(RandomMat(11, 32), 32, 1)
|| test_innerproduct_gemm(RandomMat(12, 32), 24, 1)
|| test_innerproduct_gemm(RandomMat(13, 32), 12, 1)
|| test_innerproduct_gemm(RandomMat(14, 32), 14, 1)
|| test_innerproduct_gemm(RandomMat(15, 32), 32, 1)
|| test_innerproduct_gemm(RandomMat(16, 24), 32, 1)
|| test_innerproduct_gemm(RandomMat(17, 20), 32, 1)
|| test_innerproduct_gemm(RandomMat(18, 14), 32, 1);
}
#if NCNN_INT8
static int test_innerproduct_gemm_int8(const ncnn::Mat& a, int outch, int bias, bool input_int8 = false, bool weight_int8 = false)
{
ncnn::ParamDict pd;
pd.set(0, outch);
pd.set(1, bias);
pd.set(2, outch * a.w);
pd.set(8, 1); // int8_scale_term
std::vector<ncnn::Mat> weights(bias ? 4 : 3);
const int k = a.w;
weights[0] = weight_int8 ? RandomS8Mat(outch * k) : RandomMat(outch * k);
ncnn::Mat weight_scales = weight_int8 ? RandomMat(outch, 10.f, 20.f) : scales_mat(weights[0], outch, k, k);
ncnn::Mat input_scales = scales_mat(a, 1, k, k);
ncnn::Mat a_int8 = a;
if (input_int8)
{
ncnn::Option opt;
opt.num_threads = 1;
opt.use_packing_layout = false;
ncnn::quantize_to_int8(a, a_int8, input_scales, opt);
}
if (bias)
{
weights[1] = RandomMat(outch);
weights[2] = weight_scales;
weights[3] = input_scales;
}
else
{
weights[1] = weight_scales;
weights[2] = input_scales;
}
int flag = input_int8 ? TEST_LAYER_DISABLE_AUTO_INPUT_CASTING : 0;
int ret = 0;
if (input_int8)
{
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;
ret = test_layer_opt("InnerProduct", pd, weights, opt, a_int8, 0.001f, flag);
}
else
{
ret = test_layer("InnerProduct", pd, weights, a_int8, 0.001f, flag);
}
if (ret != 0)
{
fprintf(stderr, "test_innerproduct_gemm_int8 failed a.dims=%d a=(%d %d %d) outch=%d bias=%d input_int8=%d weight_int8=%d\n", a.dims, a.w, a.h, a.c, outch, bias, input_int8, weight_int8);
}
return ret;
}
static int test_innerproduct_6()
{
return 0
|| test_innerproduct_gemm_int8(RandomMat(1, 5), 1, 1)
|| test_innerproduct_gemm_int8(RandomMat(3, 2), 2, 0)
|| test_innerproduct_gemm_int8(RandomMat(9, 8), 7, 1)
|| test_innerproduct_gemm_int8(RandomMat(2, 8), 8, 0)
|| test_innerproduct_gemm_int8(RandomMat(13, 12), 8, 1)
|| test_innerproduct_gemm_int8(RandomMat(16, 12), 16, 0)
|| test_innerproduct_gemm_int8(RandomMat(4, 15), 8, 1)
|| test_innerproduct_gemm_int8(RandomMat(6, 16), 16, 0)
|| test_innerproduct_gemm_int8(RandomMat(12, 16), 7, 1)
|| test_innerproduct_gemm_int8(RandomMat(11, 16), 8, 1, false, true)
|| test_innerproduct_gemm_int8(RandomMat(13, 15), 7, 1, true)
|| test_innerproduct_gemm_int8(RandomMat(12, 16), 7, 1, true)
|| test_innerproduct_gemm_int8(RandomMat(12, 16), 7, 1, true, true);
}
static int test_innerproduct_7()
{
return 0
|| test_innerproduct_int8(RandomMat(2, 2, 2, 1), 5, 1)
|| test_innerproduct_int8(RandomMat(3, 2, 2, 3), 7, 1)
|| test_innerproduct_int8(RandomMat(2, 2, 3, 4), 8, 1);
}
#endif // NCNN_INT8
int main()
{
SRAND(7767517);
#if NCNN_INT8
return 0
|| test_innerproduct_0()
|| test_innerproduct_1()
|| test_innerproduct_2()
|| test_innerproduct_3()
|| test_innerproduct_4()
|| test_innerproduct_5()
|| test_innerproduct_6()
|| test_innerproduct_7();
#else
return 0
|| test_innerproduct_0()
|| test_innerproduct_1()
|| test_innerproduct_2()
|| test_innerproduct_3()
|| test_innerproduct_5();
#endif
}