Upsample support NHWC (#10824)

This patch implement bilinear interpolation for Upsample/Resize 4-D input with
the outermost and innermost scale (usually channel of NHWC) as 1. It is
parallelized with output_height * output_width instead of one dimension only.

Besides, I also revert the HandleResize back to the original implementation for
TransposeOptimizerTests.TestResize* tests.

Finally, I add microbenchmark BM_NhwcUpsampleBilinear.
This commit is contained in:
Yi-Hong Lyu 2022-04-11 11:39:17 -07:00 committed by GitHub
parent 269be2fe63
commit 749c0ddd1e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 1113 additions and 346 deletions

View file

@ -873,6 +873,7 @@ if (NOT onnxruntime_ENABLE_TRAINING_TORCH_INTEROP)
${BENCHMARK_DIR}/main.cc
${BENCHMARK_DIR}/modeltest.cc
${BENCHMARK_DIR}/pooling.cc
${BENCHMARK_DIR}/resize.cc
${BENCHMARK_DIR}/batchnorm.cc
${BENCHMARK_DIR}/batchnorm2.cc
${BENCHMARK_DIR}/tptest.cc

View file

@ -967,41 +967,35 @@ static void PermuteInput(api::GraphRef& graph, api::NodeRef& node, size_t i, con
node.SetInput(i, gather_output);
}
// static bool HandleResize(HandlerArgs& args) {
// auto inputs = args.node.Inputs();
// int64_t rank_int = gsl::narrow_cast<int64_t>(args.perm.size());
//
// auto p = ChannelFirstToLastPerm(rank_int);
// auto& perm = p == args.perm ? args.perm : args.perm_inv;
// auto& perm_inv = p == args.perm ? args.perm_inv : args.perm;
//
// if (args.ctx.opset < 11) {
// PermuteInput(args.ctx.graph, args.node, 1, perm);
// } else {
// if (inputs[1] != "") {
// std::vector<int64_t> double_perm_inv = perm;
// double_perm_inv.reserve(2 * args.perm.size());
// for (int64_t p1 : perm) {
// double_perm_inv.push_back(p1 + rank_int);
// }
// PermuteInput(args.ctx.graph, args.node, 1, double_perm_inv);
// }
// for (size_t i = 2; i < inputs.size(); ++i) {
// if (inputs[i] != "") {
// PermuteInput(args.ctx.graph, args.node, i, perm);
// }
// }
// }
//
// TransposeFirstInput(args.ctx, args.node, perm);
// TransposeOutputs(args.ctx, args.node, perm_inv);
//
// SwapNodeOpTypeAndDomain(args.ctx.graph, args.node, args.node.OpType(), "com.microsoft.nhwc");
//
// return true;
// }
static bool HandleResize(HandlerArgs& args) {
auto inputs = args.node.Inputs();
int64_t rank_int = gsl::narrow_cast<int64_t>(args.perm.size());
// constexpr HandlerInfo resize_handler = {&FirstInput, &HandleResize};
if (args.ctx.opset < 11) {
PermuteInput(args.ctx.graph, args.node, 1, args.perm_inv);
} else {
if (inputs[1] != "") {
std::vector<int64_t> double_perm_inv = args.perm_inv;
double_perm_inv.reserve(2 * args.perm_inv.size());
for (int64_t p : args.perm_inv) {
double_perm_inv.push_back(p + rank_int);
}
PermuteInput(args.ctx.graph, args.node, 1, double_perm_inv);
}
for (size_t i = 2; i < inputs.size(); ++i) {
if (inputs[i] != "") {
PermuteInput(args.ctx.graph, args.node, i, args.perm_inv);
}
}
}
TransposeFirstInput(args.ctx, args.node, args.perm_inv);
TransposeOutputs(args.ctx, args.node, args.perm);
return true;
}
constexpr HandlerInfo resize_handler = {&FirstInput, &HandleResize};
static bool HandlePad(HandlerArgs& args) {
size_t rank = args.perm.size();
@ -1697,9 +1691,7 @@ static const std::unordered_map<std::string_view, const HandlerInfo&> handler_ma
{"Split", split_handler},
{"Shape", shape_handler},
{"Pad", pad_handler},
// Todo: renable resize handler after adding NHWC support in upsample op on cpu
// https://github.com/microsoft/onnxruntime/issues/9857
// {"Resize", resize_handler},
{"Resize", resize_handler},
{"ReduceSum", reduce_sum_handler},
{"ReduceLogSum", reduce_op_handler},

View file

@ -397,39 +397,24 @@ static Status UpsampleLinear(const T* input,
}
*/
struct BilinearParams {
std::vector<float> x_original;
std::vector<float> y_original;
BufferUniquePtr idx_scale_data_buffer_holder;
int64_t* input_width_mul_y1;
int64_t* input_width_mul_y2;
int64_t* in_x1;
int64_t* in_x2;
float* dx1;
float* dx2;
float* dy1;
float* dy2;
};
// The following method supports a 4-D input in 'Linear mode'
// that amounts to 'Bilinear' Upsampling/Resizing in the sense that it assumes
// the scale values for the outermost 2 dimensions are 1.
// 1. the scale values for the outermost 2 dimensions are 1 or
// 2. the scale values for the outermost and innermost dimensions are 1
// This is the common use-case where the 4-D input (batched multi-channel images)
// is usually of shape [N, C, H, W] and the scales are [1.0, 1.0, height_scale, width_scale]
static BilinearParams SetupUpsampleBilinear(int64_t input_height,
int64_t input_width,
int64_t output_height,
int64_t output_width,
float height_scale,
float width_scale,
const std::vector<float>& roi,
AllocatorPtr& alloc,
const GetOriginalCoordinateFunc& get_original_coordinate) {
// is usually of shapes:
// - [N, C, H, W] and the scales are [1.0, 1.0, height_scale, width_scale]
// - [N, H, W, C] and the scales are [1.0, height_scale, width_scale, 1.0]
BilinearParams SetupUpsampleBilinear(const int64_t input_height,
const int64_t input_width,
const int64_t output_height,
const int64_t output_width,
const float height_scale,
const float width_scale,
const std::vector<float>& roi,
AllocatorPtr& alloc,
const GetOriginalCoordinateFunc& get_original_coordinate,
bool is_nchw) {
BilinearParams p;
p.x_original.reserve(output_width);
@ -471,8 +456,9 @@ static BilinearParams SetupUpsampleBilinear(int64_t input_height,
p.dx2 = p.dx1 + output_width;
// Start processing
auto roi_y_start = roi.size() / 2 - 2;
auto roi_y_end = roi.size() - 2;
const size_t height_rindex = is_nchw ? 1 : 2;
auto roi_y_start = roi.size() / 2 - (height_rindex + 1);
auto roi_y_end = roi.size() - (height_rindex + 1);
for (int64_t y = 0; y < output_height; ++y) {
float in_y = height_scale == 1 ? static_cast<float>(y)
: get_original_coordinate(static_cast<float>(y), height_scale,
@ -496,8 +482,9 @@ static BilinearParams SetupUpsampleBilinear(int64_t input_height,
p.input_width_mul_y2[y] = input_width * in_y2;
}
auto roi_x_start = roi.size() / 2 - 1;
auto roi_x_end = roi.size() - 1;
const size_t width_rindex = is_nchw ? 0 : 1;
auto roi_x_start = roi.size() / 2 - (width_rindex + 1);
auto roi_x_end = roi.size() - (width_rindex + 1);
for (int64_t x = 0; x < output_width; ++x) {
float in_x = width_scale == 1 ? static_cast<float>(x)
: get_original_coordinate(static_cast<float>(x),
@ -522,59 +509,6 @@ static BilinearParams SetupUpsampleBilinear(int64_t input_height,
return p;
}
template <typename T>
void UpsampleBilinear(int64_t batch_size,
int64_t num_channels,
int64_t input_height,
int64_t input_width,
int64_t output_height,
int64_t output_width,
float height_scale,
float width_scale,
const std::vector<float>& roi,
bool use_extrapolation,
float extrapolation_value,
const T* XdataBase,
T* YdataBase,
AllocatorPtr& alloc,
const GetOriginalCoordinateFunc& get_original_coordinate,
concurrency::ThreadPool* tp) {
BilinearParams p = SetupUpsampleBilinear(input_height, input_width, output_height, output_width,
height_scale, width_scale, roi,
alloc, get_original_coordinate);
for (int64_t n = 0; n < batch_size; ++n) {
concurrency::ThreadPool::TrySimpleParallelFor(
tp, num_channels,
[&](std::ptrdiff_t c) {
const T* Xdata = XdataBase + (n * num_channels + c) * (input_height * input_width);
T* Ydata = YdataBase + (n * num_channels + c) * (output_height * output_width);
for (int64_t y = 0; y < output_height; ++y) {
for (int64_t x = 0; x < output_width; ++x) {
// when use_extrapolation is set and original index of x or y is out of the dim range
// then use extrapolation_value as the output value.
if (use_extrapolation &&
((p.y_original[y] < 0 || p.y_original[y] > static_cast<float>(input_height - 1)) ||
(p.x_original[x] < 0 || p.x_original[x] > static_cast<float>(input_width - 1)))) {
Ydata[output_width * y + x] = static_cast<T>(extrapolation_value);
continue;
}
T X11 = Xdata[p.input_width_mul_y1[y] + p.in_x1[x]];
T X21 = Xdata[p.input_width_mul_y1[y] + p.in_x2[x]];
T X12 = Xdata[p.input_width_mul_y2[y] + p.in_x1[x]];
T X22 = Xdata[p.input_width_mul_y2[y] + p.in_x2[x]];
Ydata[output_width * y + x] = static_cast<T>(p.dx2[x] * p.dy2[y] * X11 +
p.dx1[x] * p.dy2[y] * X21 +
p.dx2[x] * p.dy1[y] * X12 +
p.dx1[x] * p.dy1[y] * X22);
}
}
});
}
}
struct TrilinearParams {
std::vector<float> x_original;
std::vector<float> y_original;
@ -1065,25 +999,78 @@ Status Upsample<T>::BaseCompute(OpKernelContext* context,
case UpsampleMode::LINEAR: {
// Supports 'bilinear' and 'trilinear' sampling only
//'bilinear' == 2-D input or 4-D input with outermost 2 scales as 1
//'bilinear' == 2-D input or 4-D input with outermost 2 scales as 1 or
// 4-D input with outermost and innermost scales as 1
if (dims.size() == 2 || dims.size() == 4) {
bool is_2D = dims.size() == 2;
bool is_nchw = true;
const int64_t batch_size = is_2D ? 1 : dims[0];
const int64_t num_channels = is_2D ? 1 : dims[1];
const int64_t input_height = is_2D ? dims[0] : dims[2];
const int64_t input_width = is_2D ? dims[1] : dims[3];
int64_t batch_size;
int64_t num_channels;
int64_t input_height;
int64_t input_width;
const int64_t output_height = is_2D ? output_dims[0] : output_dims[2];
const int64_t output_width = is_2D ? output_dims[1] : output_dims[3];
int64_t output_height;
int64_t output_width;
float height_scale;
float width_scale;
if (is_2D) {
batch_size = 1;
num_channels = 1;
input_height = dims[0];
input_width = dims[1];
output_height = output_dims[0];
output_width = output_dims[1];
height_scale = scales[0];
width_scale = scales[1];
} else {
if (scales[1] == 1.0f) {
batch_size = dims[0];
num_channels = dims[1];
input_height = dims[2];
input_width = dims[3];
output_height = output_dims[2];
output_width = output_dims[3];
height_scale = scales[2];
width_scale = scales[3];
} else {
ORT_ENFORCE(scales[3] == 1.0f, "4-D input with innermost scale (usually channel of NHWC) as 1.");
is_nchw = false;
batch_size = dims[0];
num_channels = dims[3];
input_height = dims[1];
input_width = dims[2];
output_height = output_dims[1];
output_width = output_dims[2];
height_scale = scales[1];
width_scale = scales[2];
}
}
AllocatorPtr alloc;
ORT_RETURN_IF_ERROR(context->GetTempSpaceAllocator(&alloc));
UpsampleBilinear(batch_size, num_channels, input_height, input_width, output_height, output_width,
is_2D ? scales[0] : scales[2], is_2D ? scales[1] : scales[3], roi,
use_extrapolation_, extrapolation_value_, X->Data<T>(),
Y->MutableData<T>(), alloc, get_original_coordinate_,
output_height * output_width > 64 ? context->GetOperatorThreadPool() : nullptr);
if (is_nchw) {
UpsampleBilinear(batch_size, num_channels, input_height, input_width, output_height, output_width,
height_scale, width_scale, roi,
use_extrapolation_, extrapolation_value_, X->Data<T>(),
Y->MutableData<T>(), alloc, get_original_coordinate_,
output_height * output_width > 64 ? context->GetOperatorThreadPool() : nullptr);
} else {
NhwcUpsampleBilinear(batch_size, num_channels, input_height, input_width, output_height, output_width,
height_scale, width_scale, roi,
use_extrapolation_, extrapolation_value_, X->Data<T>(),
Y->MutableData<T>(), alloc, get_original_coordinate_,
output_height * output_width * num_channels > 64 ? context->GetOperatorThreadPool() : nullptr);
}
return Status::OK();
} else if (dims.size() == 3 || dims.size() == 5) {
//'trilinear' == 3-D input or 5-D input with outermost 2 scales as 1

View file

@ -50,6 +50,25 @@ enum ResizeNearestMode {
NearestModeCount = 5,
};
struct BilinearParams {
std::vector<float> x_original;
std::vector<float> y_original;
BufferUniquePtr idx_scale_data_buffer_holder;
int64_t* input_width_mul_y1;
int64_t* input_width_mul_y2;
int64_t* in_x1;
int64_t* in_x2;
float* dx1;
float* dx2;
float* dy1;
float* dy2;
};
class UpsampleBase {
protected:
UpsampleBase(const OpKernelInfo& info) : scales_cached_(false), roi_cached_(false), use_extrapolation_(false) {
@ -378,7 +397,125 @@ class Upsample : public UpsampleBase, public OpKernel {
const gsl::span<const int64_t>& output_dims) const;
};
BilinearParams SetupUpsampleBilinear(const int64_t input_height,
const int64_t input_width,
const int64_t output_height,
const int64_t output_width,
const float height_scale,
const float width_scale,
const std::vector<float>& roi,
AllocatorPtr& alloc,
const GetOriginalCoordinateFunc& get_original_coordinate,
bool is_nchw);
template <typename T>
void UpsampleBilinear(const int64_t batch_size,
const int64_t num_channels,
const int64_t input_height,
const int64_t input_width,
const int64_t output_height,
const int64_t output_width,
const float height_scale,
const float width_scale,
const std::vector<float>& roi,
const bool use_extrapolation,
const float extrapolation_value,
const T* const XdataBase,
T* const YdataBase,
AllocatorPtr& alloc,
const GetOriginalCoordinateFunc& get_original_coordinate,
concurrency::ThreadPool* tp) {
BilinearParams p = SetupUpsampleBilinear(input_height, input_width, output_height, output_width,
height_scale, width_scale, roi,
alloc, get_original_coordinate, true);
for (int64_t n = 0; n < batch_size; ++n) {
concurrency::ThreadPool::TrySimpleParallelFor(
tp, num_channels,
[&](std::ptrdiff_t c) {
const T* Xdata = XdataBase + (n * num_channels + c) * (input_height * input_width);
T* Ydata = YdataBase + (n * num_channels + c) * (output_height * output_width);
for (int64_t y = 0; y < output_height; ++y) {
for (int64_t x = 0; x < output_width; ++x) {
// when use_extrapolation is set and original index of x or y is out of the dim range
// then use extrapolation_value as the output value.
if (use_extrapolation &&
((p.y_original[y] < 0 || p.y_original[y] > static_cast<float>(input_height - 1)) ||
(p.x_original[x] < 0 || p.x_original[x] > static_cast<float>(input_width - 1)))) {
Ydata[output_width * y + x] = static_cast<T>(extrapolation_value);
continue;
}
T X11 = Xdata[p.input_width_mul_y1[y] + p.in_x1[x]];
T X21 = Xdata[p.input_width_mul_y1[y] + p.in_x2[x]];
T X12 = Xdata[p.input_width_mul_y2[y] + p.in_x1[x]];
T X22 = Xdata[p.input_width_mul_y2[y] + p.in_x2[x]];
Ydata[output_width * y + x] = static_cast<T>(p.dx2[x] * p.dy2[y] * X11 +
p.dx1[x] * p.dy2[y] * X21 +
p.dx2[x] * p.dy1[y] * X12 +
p.dx1[x] * p.dy1[y] * X22);
}
}
});
}
}
template <typename T>
void NhwcUpsampleBilinear(const int64_t batch_size,
const int64_t num_channels,
const int64_t input_height,
const int64_t input_width,
const int64_t output_height,
const int64_t output_width,
const float height_scale,
const float width_scale,
const std::vector<float>& roi,
const bool use_extrapolation,
const float extrapolation_value,
const T* const XdataBase,
T* const YdataBase,
AllocatorPtr& alloc,
const GetOriginalCoordinateFunc& get_original_coordinate,
concurrency::ThreadPool* tp) {
BilinearParams p = SetupUpsampleBilinear(input_height, input_width, output_height, output_width,
height_scale, width_scale, roi,
alloc, get_original_coordinate, false);
for (int64_t n = 0; n < batch_size; ++n) {
const T* Xdata = XdataBase + n * (input_height * input_width) * num_channels;
T* Ydata = YdataBase + n * (output_height * output_width) * num_channels;
concurrency::ThreadPool::TryParallelFor(
tp, output_height * output_width,
static_cast<double>(num_channels * 2),
[&](std::ptrdiff_t first, std::ptrdiff_t last) {
for (std::ptrdiff_t i = first; i < last; ++i) {
const int64_t x = i % output_width;
const int64_t y = i / output_width;
for (int64_t c = 0; c < num_channels; ++c) {
// when use_extrapolation is set and original index of x or y is out of the dim range
// then use extrapolation_value as the output value.
if (use_extrapolation &&
((p.y_original[y] < 0 || p.y_original[y] > static_cast<float>(input_height - 1)) ||
(p.x_original[x] < 0 || p.x_original[x] > static_cast<float>(input_width - 1)))) {
Ydata[(output_width * y + x) * num_channels + c] = static_cast<T>(extrapolation_value);
continue;
}
T X11 = Xdata[(p.input_width_mul_y1[y] + p.in_x1[x]) * num_channels + c];
T X21 = Xdata[(p.input_width_mul_y1[y] + p.in_x2[x]) * num_channels + c];
T X12 = Xdata[(p.input_width_mul_y2[y] + p.in_x1[x]) * num_channels + c];
T X22 = Xdata[(p.input_width_mul_y2[y] + p.in_x2[x]) * num_channels + c];
Ydata[(output_width * y + x) * num_channels + c] = static_cast<T>(p.dx2[x] * p.dy2[y] * X11 +
p.dx1[x] * p.dy2[y] * X21 +
p.dx2[x] * p.dy1[y] * X12 +
p.dx1[x] * p.dy1[y] * X22);
}
}
});
}
}
} // namespace onnxruntime
#if defined(_MSC_VER) && !defined(__clang__)
#pragma warning(pop)
#endif
#endif

View file

@ -0,0 +1,83 @@
#include <limits>
#include "common.h"
#include <benchmark/benchmark.h>
#include "core/common/safeint.h"
#include "core/framework/allocator.h"
#include "core/mlas/lib/mlasi.h"
#include "core/providers/cpu/tensor/upsample.h"
#include "core/util/math_cpuonly.h"
#include "core/util/qmath.h"
#include "core/util/thread_utils.h"
using namespace onnxruntime;
template <typename T>
static void BM_NhwcUpsampleBilinear(benchmark::State& state) {
const int64_t output_height = static_cast<int64_t>(state.range(0));
const int64_t output_width = static_cast<int64_t>(state.range(1));
constexpr int64_t batch_size = 1;
constexpr int64_t num_channels = 256;
constexpr int64_t input_height = 32;
constexpr int64_t input_width = 32;
const int64_t height_scale = output_height / input_height;
const int64_t width_scale = output_width / input_width;
const std::vector<float> roi{0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f};
constexpr bool use_extrapolation = false;
constexpr float extrapolation_value = 0;
constexpr size_t XdataBaseSize = batch_size * num_channels * input_height * input_width;
const T* const XdataBase = GenerateArrayWithRandomValue<T>(XdataBaseSize, std::numeric_limits<T>::min(), std::numeric_limits<T>::max());
const size_t YdataBaseSize = batch_size * num_channels * output_height * output_width;
T* const YdataBase = (T*)aligned_alloc(sizeof(T) * YdataBaseSize, 64);
AllocatorPtr alloc = std::make_shared<CPUAllocator>();
const GetOriginalCoordinateFunc& get_original_coordinate =
[](float x_resized, float x_scale, float, float, float, float) {
return x_resized / x_scale;
};
OrtThreadPoolParams tpo;
tpo.auto_set_affinity = true;
std::unique_ptr<concurrency::ThreadPool> tp(
concurrency::CreateThreadPool(&onnxruntime::Env::Default(), tpo, concurrency::ThreadPoolType::INTRA_OP));
for (auto _ : state) {
NhwcUpsampleBilinear(batch_size, num_channels, input_height, input_width, output_height, output_width,
static_cast<float>(height_scale), static_cast<float>(width_scale), roi,
use_extrapolation, extrapolation_value, XdataBase,
YdataBase, alloc, get_original_coordinate,
output_height * output_width * num_channels > 64 ? tp.get() : nullptr);
}
}
BENCHMARK_TEMPLATE(BM_NhwcUpsampleBilinear, uint8_t)
->MeasureProcessCPUTime()
->UseRealTime()
->Unit(benchmark::TimeUnit::kNanosecond)
->Args({32, 32})
->Args({64, 64})
->Args({96, 96})
->Args({128, 128})
->Args({160, 160})
->Args({1, 1000000});
BENCHMARK_TEMPLATE(BM_NhwcUpsampleBilinear, int8_t)
->MeasureProcessCPUTime()
->UseRealTime()
->Unit(benchmark::TimeUnit::kNanosecond)
->Args({32, 32})
->Args({64, 64})
->Args({96, 96})
->Args({128, 128})
->Args({160, 160})
->Args({1, 1000000});
BENCHMARK_TEMPLATE(BM_NhwcUpsampleBilinear, float)
->MeasureProcessCPUTime()
->UseRealTime()
->Unit(benchmark::TimeUnit::kNanosecond)
->Args({32, 32})
->Args({64, 64})
->Args({96, 96})
->Args({128, 128})
->Args({160, 160})
->Args({1, 1000000});

View file

@ -291,212 +291,209 @@ TEST(TransposeOptimizerTests, TestPadNonconst) {
/*opset_version*/ 11);
}
// Todo: renable tests on resize transformer after adding NHWC support in upsample op on cpu
// https://github.com/microsoft/onnxruntime/issues/9857
TEST(TransposeOptimizerTests, TestResize) {
auto build_test_case_1 = [&](ModelTestBuilder& builder) {
auto* input0_arg = MakeInput<float>(builder, {{4, -1, 2, -1}}, {4, 6, 2, 10}, 0.0, 1.0);
auto* const_1 = builder.MakeInitializer<float>({4}, {0.3f, 2.5f, 1.0f, 0.7f});
auto* transpose_1_out_0 = builder.MakeIntermediate();
auto* resize_1_out_0 = builder.MakeIntermediate();
auto* transpose_2_out_0 = builder.MakeOutput();
// TEST(TransposeOptimizerTests, TestResize) {
// auto build_test_case_1 = [&](ModelTestBuilder& builder) {
// auto* input0_arg = MakeInput<float>(builder, {{4, -1, 2, -1}}, {4, 6, 2, 10}, 0.0, 1.0);
// auto* const_1 = builder.MakeInitializer<float>({4}, {0.3f, 2.5f, 1.0f, 0.7f});
// auto* transpose_1_out_0 = builder.MakeIntermediate();
// auto* resize_1_out_0 = builder.MakeIntermediate();
// auto* transpose_2_out_0 = builder.MakeOutput();
//
// auto& transpose_1 = builder.AddNode("Transpose", {input0_arg}, {transpose_1_out_0});
// transpose_1.AddAttribute("perm", std::vector<int64_t>{0, 3, 1, 2});
// builder.AddNode("Resize", {transpose_1_out_0, const_1}, {resize_1_out_0});
// auto& transpose_2 = builder.AddNode("Transpose", {resize_1_out_0}, {transpose_2_out_0});
// transpose_2.AddAttribute("perm", std::vector<int64_t>{0, 2, 3, 1});
// };
//
// auto check_optimized_graph_1 = [&](InferenceSessionWrapper& session) {
// int transpose_cost = EstimateTransposeCost(session.GetGraph());
// EXPECT_EQ(transpose_cost, 0);
// };
//
// TransformerTester(build_test_case_1,
// check_optimized_graph_1,
// TransformerLevel::Default,
// TransformerLevel::Level1,
// /*opset_version*/ 10);
// }
//
// TEST(TransposeOptimizerTests, TestResizeOpset11) {
// auto build_test_case_1 = [&](ModelTestBuilder& builder) {
// auto* input0_arg = MakeInput<float>(builder, {{4, -1, 2, -1}}, {4, 6, 2, 10}, 0.0, 1.0);
// auto* const_1 = builder.MakeInitializer<float>({8}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f});
// auto* const_2 = builder.MakeInitializer<float>({4}, {0.3f, 2.5f, 1.0f, 0.7f});
// auto* transpose_1_out_0 = builder.MakeIntermediate();
// auto* resize_1_out_0 = builder.MakeIntermediate();
// auto* transpose_2_out_0 = builder.MakeOutput();
//
// auto& transpose_1 = builder.AddNode("Transpose", {input0_arg}, {transpose_1_out_0});
// transpose_1.AddAttribute("perm", std::vector<int64_t>{0, 3, 1, 2});
// builder.AddNode("Resize", {transpose_1_out_0, const_1, const_2}, {resize_1_out_0});
// auto& transpose_2 = builder.AddNode("Transpose", {resize_1_out_0}, {transpose_2_out_0});
// transpose_2.AddAttribute("perm", std::vector<int64_t>{0, 2, 3, 1});
// };
//
// auto check_optimized_graph_1 = [&](InferenceSessionWrapper& session) {
// int transpose_cost = EstimateTransposeCost(session.GetGraph());
// EXPECT_EQ(transpose_cost, 0);
// };
//
// TransformerTester(build_test_case_1,
// check_optimized_graph_1,
// TransformerLevel::Default,
// TransformerLevel::Level1,
// /*opset_version*/ 11);
// }
//
// TEST(TransposeOptimizerTests, TestResizeOpset15) {
// auto build_test_case_1 = [&](ModelTestBuilder& builder) {
// auto* input0_arg = MakeInput<float>(builder, {{4, -1, 2, -1}}, {4, 6, 2, 10}, 0.0, 1.0);
// auto* const_1 = builder.MakeInitializer<float>({4}, {0.3f, 2.5f, 1.0f, 0.7f});
// auto* transpose_1_out_0 = builder.MakeIntermediate();
// auto* resize_1_out_0 = builder.MakeIntermediate();
// auto* transpose_2_out_0 = builder.MakeOutput();
// auto empty_arg = NodeArg("", nullptr);
//
// auto& transpose_1 = builder.AddNode("Transpose", {input0_arg}, {transpose_1_out_0});
// transpose_1.AddAttribute("perm", std::vector<int64_t>{0, 3, 1, 2});
// builder.AddNode("Resize", {transpose_1_out_0, &empty_arg, const_1}, {resize_1_out_0});
// auto& transpose_2 = builder.AddNode("Transpose", {resize_1_out_0}, {transpose_2_out_0});
// transpose_2.AddAttribute("perm", std::vector<int64_t>{0, 2, 3, 1});
// };
//
// auto check_optimized_graph_1 = [&](InferenceSessionWrapper& session) {
// int transpose_cost = EstimateTransposeCost(session.GetGraph());
// EXPECT_EQ(transpose_cost, 0);
// };
//
// TransformerTester(build_test_case_1,
// check_optimized_graph_1,
// TransformerLevel::Default,
// TransformerLevel::Level1,
// /*opset_version*/ 15);
// }
//
// TEST(TransposeOptimizerTests, TestResizeSizeRoi) {
// auto build_test_case_1 = [&](ModelTestBuilder& builder) {
// auto* input0_arg = MakeInput<float>(builder, {{4, -1, 2, -1}}, {4, 6, 2, 10}, 0.0, 1.0);
// auto* const_1 = builder.MakeInitializer<float>({8}, {0.1f, 0.2f, 0.3f, 0.4f, 0.9f, 0.8f, 0.7f, 0.6f});
// auto* const_2 = builder.MakeInitializer<int64_t>({4}, {10, 9, 8, 7});
// auto* transpose_1_out_0 = builder.MakeIntermediate();
// auto* resize_1_out_0 = builder.MakeIntermediate();
// auto* transpose_2_out_0 = builder.MakeOutput();
// auto empty_arg = NodeArg("", nullptr);
//
// auto& transpose_1 = builder.AddNode("Transpose", {input0_arg}, {transpose_1_out_0});
// transpose_1.AddAttribute("perm", std::vector<int64_t>{0, 3, 1, 2});
// auto& resize_1 = builder.AddNode("Resize", {transpose_1_out_0, const_1, &empty_arg, const_2}, {resize_1_out_0});
// resize_1.AddAttribute("coordinate_transformation_mode", "tf_crop_and_resize");
// auto& transpose_2 = builder.AddNode("Transpose", {resize_1_out_0}, {transpose_2_out_0});
// transpose_2.AddAttribute("perm", std::vector<int64_t>{0, 2, 3, 1});
// };
//
// auto check_optimized_graph_1 = [&](InferenceSessionWrapper& session) {
// int transpose_cost = EstimateTransposeCost(session.GetGraph());
// EXPECT_EQ(transpose_cost, 0);
// };
//
// TransformerTester(build_test_case_1,
// check_optimized_graph_1,
// TransformerLevel::Default,
// TransformerLevel::Level1,
// /*opset_version*/ 15);
// }
//
// TEST(TransposeOptimizerTests, TestResizeRoiScalesZeroRank0) {
// auto build_test_case_1 = [&](ModelTestBuilder& builder) {
// auto* input = builder.MakeInput<uint8_t>({1, 512, 512, 3},
// std::numeric_limits<uint8_t>::min(),
// std::numeric_limits<uint8_t>::max());
// auto* resize_in_roi = builder.MakeInitializer<float>({0}, {});
// auto* resize_in_scales = builder.MakeInitializer<float>({0}, {});
// auto* resize_in_sizes = builder.MakeInitializer<int64_t>({4}, {1, 256, 32, 32});
//
// auto* transpose1_out_transposed = builder.MakeIntermediate();
// auto* resize_out_Y = builder.MakeIntermediate();
// auto* output = builder.MakeOutput();
//
// auto& transpose_1 = builder.AddNode("Transpose", {input}, {transpose1_out_transposed});
// transpose_1.AddAttribute("perm", std::vector<int64_t>{0, 3, 1, 2});
// builder.AddNode("Resize",
// {transpose1_out_transposed, resize_in_roi, resize_in_scales, resize_in_sizes},
// {resize_out_Y});
// auto& transpose_2 = builder.AddNode("Transpose", {resize_out_Y}, {output});
// transpose_2.AddAttribute("perm", std::vector<int64_t>{0, 2, 3, 1});
// };
//
// auto check_optimized_graph_1 = [&](InferenceSessionWrapper& session) {
// int transpose_cost = EstimateTransposeCost(session.GetGraph());
// EXPECT_EQ(transpose_cost, 0);
// };
//
// TransformerTester(build_test_case_1,
// check_optimized_graph_1,
// TransformerLevel::Default,
// TransformerLevel::Level1);
// }
//
// TEST(TransposeOptimizerTests, TestResizeNonconst) {
// auto build_test_case_1 = [&](ModelTestBuilder& builder) {
// auto* input0_arg = MakeInput<float>(builder, {{4, -1, 2, -1}}, {4, 6, 2, 10}, 0.0, 1.0);
// auto* input1_arg = MakeInput<float>(builder, {{8}}, {8}, {0.1f, 0.2f, 0.3f, 0.4f, 0.9f, 0.8f, 0.7f, 0.6f});
// auto* input2_arg = MakeInput<float>(builder, {{4}}, {4}, {0.3f, 2.5f, 1.0f, 0.7f});
// auto* transpose_1_out_0 = builder.MakeIntermediate();
// auto* resize_1_out_0 = builder.MakeIntermediate();
// auto* transpose_2_out_0 = builder.MakeOutput();
//
// auto& transpose_1 = builder.AddNode("Transpose", {input0_arg}, {transpose_1_out_0});
// transpose_1.AddAttribute("perm", std::vector<int64_t>{0, 3, 1, 2});
// auto& resize_1 = builder.AddNode("Resize", {transpose_1_out_0, input1_arg, input2_arg}, {resize_1_out_0});
// resize_1.AddAttribute("coordinate_transformation_mode", "tf_crop_and_resize");
// auto& transpose_2 = builder.AddNode("Transpose", {resize_1_out_0}, {transpose_2_out_0});
// transpose_2.AddAttribute("perm", std::vector<int64_t>{0, 2, 3, 1});
// };
//
// auto check_optimized_graph_1 = [&](InferenceSessionWrapper& session) {
// int transpose_cost = EstimateTransposeCost(session.GetGraph());
// EXPECT_EQ(transpose_cost, 0);
// };
//
// TransformerTester(build_test_case_1,
// check_optimized_graph_1,
// TransformerLevel::Default,
// TransformerLevel::Level1,
// /*opset_version*/ 11);
// }
//
// TEST(TransposeOptimizerTests, TestResizeNonconstOpset13) {
// auto build_test_case_1 = [&](ModelTestBuilder& builder) {
// auto* input0_arg = MakeInput<float>(builder, {{4, -1, 2, -1}}, {4, 6, 2, 10}, 0.0, 1.0);
// auto* input1_arg = MakeInput<float>(builder, {{8}}, {8}, {0.1f, 0.2f, 0.3f, 0.4f, 0.9f, 0.8f, 0.7f, 0.6f});
// auto* input2_arg = MakeInput<float>(builder, {{4}}, {4}, {0.3f, 2.5f, 1.0f, 0.7f});
// auto* transpose_1_out_0 = builder.MakeIntermediate();
// auto* resize_1_out_0 = builder.MakeIntermediate();
// auto* transpose_2_out_0 = builder.MakeOutput();
//
// auto& transpose_1 = builder.AddNode("Transpose", {input0_arg}, {transpose_1_out_0});
// transpose_1.AddAttribute("perm", std::vector<int64_t>{0, 3, 1, 2});
// auto& resize_1 = builder.AddNode("Resize", {transpose_1_out_0, input1_arg, input2_arg}, {resize_1_out_0});
// resize_1.AddAttribute("coordinate_transformation_mode", "tf_crop_and_resize");
// auto& transpose_2 = builder.AddNode("Transpose", {resize_1_out_0}, {transpose_2_out_0});
// transpose_2.AddAttribute("perm", std::vector<int64_t>{0, 2, 3, 1});
// };
//
// auto check_optimized_graph_1 = [&](InferenceSessionWrapper& session) {
// int transpose_cost = EstimateTransposeCost(session.GetGraph());
// EXPECT_EQ(transpose_cost, 0);
// };
//
// TransformerTester(build_test_case_1,
// check_optimized_graph_1,
// TransformerLevel::Default,
// TransformerLevel::Level1,
// /*opset_version*/ 13);
// }
auto& transpose_1 = builder.AddNode("Transpose", {input0_arg}, {transpose_1_out_0});
transpose_1.AddAttribute("perm", std::vector<int64_t>{0, 3, 1, 2});
builder.AddNode("Resize", {transpose_1_out_0, const_1}, {resize_1_out_0});
auto& transpose_2 = builder.AddNode("Transpose", {resize_1_out_0}, {transpose_2_out_0});
transpose_2.AddAttribute("perm", std::vector<int64_t>{0, 2, 3, 1});
};
auto check_optimized_graph_1 = [&](InferenceSessionWrapper& session) {
int transpose_cost = EstimateTransposeCost(session.GetGraph());
EXPECT_EQ(transpose_cost, 0);
};
TransformerTester(build_test_case_1,
check_optimized_graph_1,
TransformerLevel::Default,
TransformerLevel::Level1,
/*opset_version*/ 10);
}
TEST(TransposeOptimizerTests, TestResizeOpset11) {
auto build_test_case_1 = [&](ModelTestBuilder& builder) {
auto* input0_arg = MakeInput<float>(builder, {{4, -1, 2, -1}}, {4, 6, 2, 10}, 0.0, 1.0);
auto* const_1 = builder.MakeInitializer<float>({8}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f});
auto* const_2 = builder.MakeInitializer<float>({4}, {0.3f, 2.5f, 1.0f, 0.7f});
auto* transpose_1_out_0 = builder.MakeIntermediate();
auto* resize_1_out_0 = builder.MakeIntermediate();
auto* transpose_2_out_0 = builder.MakeOutput();
auto& transpose_1 = builder.AddNode("Transpose", {input0_arg}, {transpose_1_out_0});
transpose_1.AddAttribute("perm", std::vector<int64_t>{0, 3, 1, 2});
builder.AddNode("Resize", {transpose_1_out_0, const_1, const_2}, {resize_1_out_0});
auto& transpose_2 = builder.AddNode("Transpose", {resize_1_out_0}, {transpose_2_out_0});
transpose_2.AddAttribute("perm", std::vector<int64_t>{0, 2, 3, 1});
};
auto check_optimized_graph_1 = [&](InferenceSessionWrapper& session) {
int transpose_cost = EstimateTransposeCost(session.GetGraph());
EXPECT_EQ(transpose_cost, 0);
};
TransformerTester(build_test_case_1,
check_optimized_graph_1,
TransformerLevel::Default,
TransformerLevel::Level1,
/*opset_version*/ 11);
}
TEST(TransposeOptimizerTests, TestResizeOpset15) {
auto build_test_case_1 = [&](ModelTestBuilder& builder) {
auto* input0_arg = MakeInput<float>(builder, {{4, -1, 2, -1}}, {4, 6, 2, 10}, 0.0, 1.0);
auto* const_1 = builder.MakeInitializer<float>({4}, {0.3f, 2.5f, 1.0f, 0.7f});
auto* transpose_1_out_0 = builder.MakeIntermediate();
auto* resize_1_out_0 = builder.MakeIntermediate();
auto* transpose_2_out_0 = builder.MakeOutput();
auto empty_arg = NodeArg("", nullptr);
auto& transpose_1 = builder.AddNode("Transpose", {input0_arg}, {transpose_1_out_0});
transpose_1.AddAttribute("perm", std::vector<int64_t>{0, 3, 1, 2});
builder.AddNode("Resize", {transpose_1_out_0, &empty_arg, const_1}, {resize_1_out_0});
auto& transpose_2 = builder.AddNode("Transpose", {resize_1_out_0}, {transpose_2_out_0});
transpose_2.AddAttribute("perm", std::vector<int64_t>{0, 2, 3, 1});
};
auto check_optimized_graph_1 = [&](InferenceSessionWrapper& session) {
int transpose_cost = EstimateTransposeCost(session.GetGraph());
EXPECT_EQ(transpose_cost, 0);
};
TransformerTester(build_test_case_1,
check_optimized_graph_1,
TransformerLevel::Default,
TransformerLevel::Level1,
/*opset_version*/ 15);
}
TEST(TransposeOptimizerTests, TestResizeSizeRoi) {
auto build_test_case_1 = [&](ModelTestBuilder& builder) {
auto* input0_arg = MakeInput<float>(builder, {{4, -1, 2, -1}}, {4, 6, 2, 10}, 0.0, 1.0);
auto* const_1 = builder.MakeInitializer<float>({8}, {0.1f, 0.2f, 0.3f, 0.4f, 0.9f, 0.8f, 0.7f, 0.6f});
auto* const_2 = builder.MakeInitializer<int64_t>({4}, {10, 9, 8, 7});
auto* transpose_1_out_0 = builder.MakeIntermediate();
auto* resize_1_out_0 = builder.MakeIntermediate();
auto* transpose_2_out_0 = builder.MakeOutput();
auto empty_arg = NodeArg("", nullptr);
auto& transpose_1 = builder.AddNode("Transpose", {input0_arg}, {transpose_1_out_0});
transpose_1.AddAttribute("perm", std::vector<int64_t>{0, 3, 1, 2});
auto& resize_1 = builder.AddNode("Resize", {transpose_1_out_0, const_1, &empty_arg, const_2}, {resize_1_out_0});
resize_1.AddAttribute("coordinate_transformation_mode", "tf_crop_and_resize");
auto& transpose_2 = builder.AddNode("Transpose", {resize_1_out_0}, {transpose_2_out_0});
transpose_2.AddAttribute("perm", std::vector<int64_t>{0, 2, 3, 1});
};
auto check_optimized_graph_1 = [&](InferenceSessionWrapper& session) {
int transpose_cost = EstimateTransposeCost(session.GetGraph());
EXPECT_EQ(transpose_cost, 0);
};
TransformerTester(build_test_case_1,
check_optimized_graph_1,
TransformerLevel::Default,
TransformerLevel::Level1,
/*opset_version*/ 15);
}
TEST(TransposeOptimizerTests, TestResizeRoiScalesZeroRank0) {
auto build_test_case_1 = [&](ModelTestBuilder& builder) {
auto* input = builder.MakeInput<uint8_t>({1, 512, 512, 3},
std::numeric_limits<uint8_t>::min(),
std::numeric_limits<uint8_t>::max());
auto* resize_in_roi = builder.MakeInitializer<float>({0}, {});
auto* resize_in_scales = builder.MakeInitializer<float>({0}, {});
auto* resize_in_sizes = builder.MakeInitializer<int64_t>({4}, {1, 256, 32, 32});
auto* transpose1_out_transposed = builder.MakeIntermediate();
auto* resize_out_Y = builder.MakeIntermediate();
auto* output = builder.MakeOutput();
auto& transpose_1 = builder.AddNode("Transpose", {input}, {transpose1_out_transposed});
transpose_1.AddAttribute("perm", std::vector<int64_t>{0, 3, 1, 2});
builder.AddNode("Resize",
{transpose1_out_transposed, resize_in_roi, resize_in_scales, resize_in_sizes},
{resize_out_Y});
auto& transpose_2 = builder.AddNode("Transpose", {resize_out_Y}, {output});
transpose_2.AddAttribute("perm", std::vector<int64_t>{0, 2, 3, 1});
};
auto check_optimized_graph_1 = [&](InferenceSessionWrapper& session) {
int transpose_cost = EstimateTransposeCost(session.GetGraph());
EXPECT_EQ(transpose_cost, 0);
};
TransformerTester(build_test_case_1,
check_optimized_graph_1,
TransformerLevel::Default,
TransformerLevel::Level1);
}
TEST(TransposeOptimizerTests, TestResizeNonconst) {
auto build_test_case_1 = [&](ModelTestBuilder& builder) {
auto* input0_arg = MakeInput<float>(builder, {{4, -1, 2, -1}}, {4, 6, 2, 10}, 0.0, 1.0);
auto* input1_arg = MakeInput<float>(builder, {{8}}, {8}, {0.1f, 0.2f, 0.3f, 0.4f, 0.9f, 0.8f, 0.7f, 0.6f});
auto* input2_arg = MakeInput<float>(builder, {{4}}, {4}, {0.3f, 2.5f, 1.0f, 0.7f});
auto* transpose_1_out_0 = builder.MakeIntermediate();
auto* resize_1_out_0 = builder.MakeIntermediate();
auto* transpose_2_out_0 = builder.MakeOutput();
auto& transpose_1 = builder.AddNode("Transpose", {input0_arg}, {transpose_1_out_0});
transpose_1.AddAttribute("perm", std::vector<int64_t>{0, 3, 1, 2});
auto& resize_1 = builder.AddNode("Resize", {transpose_1_out_0, input1_arg, input2_arg}, {resize_1_out_0});
resize_1.AddAttribute("coordinate_transformation_mode", "tf_crop_and_resize");
auto& transpose_2 = builder.AddNode("Transpose", {resize_1_out_0}, {transpose_2_out_0});
transpose_2.AddAttribute("perm", std::vector<int64_t>{0, 2, 3, 1});
};
auto check_optimized_graph_1 = [&](InferenceSessionWrapper& session) {
int transpose_cost = EstimateTransposeCost(session.GetGraph());
EXPECT_EQ(transpose_cost, 0);
};
TransformerTester(build_test_case_1,
check_optimized_graph_1,
TransformerLevel::Default,
TransformerLevel::Level1,
/*opset_version*/ 11);
}
TEST(TransposeOptimizerTests, TestResizeNonconstOpset13) {
auto build_test_case_1 = [&](ModelTestBuilder& builder) {
auto* input0_arg = MakeInput<float>(builder, {{4, -1, 2, -1}}, {4, 6, 2, 10}, 0.0, 1.0);
auto* input1_arg = MakeInput<float>(builder, {{8}}, {8}, {0.1f, 0.2f, 0.3f, 0.4f, 0.9f, 0.8f, 0.7f, 0.6f});
auto* input2_arg = MakeInput<float>(builder, {{4}}, {4}, {0.3f, 2.5f, 1.0f, 0.7f});
auto* transpose_1_out_0 = builder.MakeIntermediate();
auto* resize_1_out_0 = builder.MakeIntermediate();
auto* transpose_2_out_0 = builder.MakeOutput();
auto& transpose_1 = builder.AddNode("Transpose", {input0_arg}, {transpose_1_out_0});
transpose_1.AddAttribute("perm", std::vector<int64_t>{0, 3, 1, 2});
auto& resize_1 = builder.AddNode("Resize", {transpose_1_out_0, input1_arg, input2_arg}, {resize_1_out_0});
resize_1.AddAttribute("coordinate_transformation_mode", "tf_crop_and_resize");
auto& transpose_2 = builder.AddNode("Transpose", {resize_1_out_0}, {transpose_2_out_0});
transpose_2.AddAttribute("perm", std::vector<int64_t>{0, 2, 3, 1});
};
auto check_optimized_graph_1 = [&](InferenceSessionWrapper& session) {
int transpose_cost = EstimateTransposeCost(session.GetGraph());
EXPECT_EQ(transpose_cost, 0);
};
TransformerTester(build_test_case_1,
check_optimized_graph_1,
TransformerLevel::Default,
TransformerLevel::Level1,
/*opset_version*/ 13);
}
TEST(TransposeOptimizerTests, TestAdd) {
auto build_test_case_1 = [&](ModelTestBuilder& builder) {
@ -4013,6 +4010,5 @@ TEST(TransposeOptimizerTests, RegressionTest_GitHubIssue10305) {
ASSERT_STATUS_OK(session_object.Load(model_uri));
ASSERT_STATUS_OK(session_object.Initialize()); // optimizers run during initialization
}
} // namespace test
} // namespace onnxruntime

View file

@ -65,6 +65,38 @@ TEST(ResizeOpTest, ResizeOpLinearDownSampleTest_tf_crop_and_resize_with_extrapol
test.Run();
}
TEST(ResizeOpTest, NhwcResizeOpLinearDownSampleTest_tf_crop_and_resize_with_extrapolation) {
OpTester test("Resize", 13);
std::vector<float> scales{1.0f, 0.8f, 0.8f, 1.0f};
std::vector<float> roi{0.0f, 0.4f, 0.6f, 0.0f, 1.0f, 1.2f, 1.7f, 1.0f};
test.AddAttribute("mode", "linear");
test.AddAttribute("coordinate_transformation_mode", "tf_crop_and_resize");
test.AddAttribute("extrapolation_value", 10.0f);
constexpr int64_t N = 1, H = 4, W = 4, C = 1;
std::vector<float> X = {
1.0f, 2.0f, 3.0f, 4.0f,
5.0f, 6.0f, 7.0f, 8.0f,
9.0f, 10.0f, 11.0f, 12.0f,
13.0f, 14.0f, 15.0f, 16.0f};
test.AddInput<float>("X", {N, H, W, C}, X);
test.AddInput<float>("roi", {8}, roi);
test.AddInput<float>("scales", {4}, scales);
std::vector<float> Y = {7.6000004f, 10.0f, 10.0f,
12.400001f, 10.f, 10.0f,
10.0f, 10.0f, 10.0f};
test.AddOutput<float>("Y", {N, static_cast<int64_t>(H * scales[1]), static_cast<int64_t>(W * scales[2]), C}, Y);
//CUDA: result mismatch due to not implementing NHWC support
//TensorRT: results mismatch
//ROCm: results mismatch
test.Run(OpTester::ExpectResult::kExpectSuccess, "",
{kCudaExecutionProvider, kTensorrtExecutionProvider, kRocmExecutionProvider});
}
TEST(ResizeOpTest, ResizeOpLinearDownSampleTest_4DBilinear) {
OpTester test("Resize", 13);
std::vector<float> roi{};
@ -689,10 +721,10 @@ class ResizeOpTester : public OpTester {
OpTester::AddNodes(graph, graph_input_defs, graph_output_defs, add_attribute_funcs);
// set the Graph inputs to just X and roi (exclude 'scales') so the 'scales' are a constant initializer
if(scales_in_initializer_) {
if (scales_in_initializer_) {
graph.SetInputs({graph.GetNodeArg(graph_input_defs[0]->Name()),
graph.GetNodeArg(graph_input_defs[1]->Name())});
if(sizes_in_initializer_) {
if (sizes_in_initializer_) {
ASSERT_TRUE(graph_input_defs.size() == 4);
} else {
ASSERT_TRUE(graph_input_defs.size() == 3);

View file

@ -42,6 +42,59 @@ TEST(UpsampleOpTest, UpsampleOpNearestTest) {
test.Run();
}
TEST(UpsampleOpTest, NhwcUpsampleOpNearestTest) {
OpTester test("Upsample");
std::vector<float> scales{1.0f, 2.0f, 3.0f, 1.0f};
test.AddAttribute("mode", "nearest");
test.AddAttribute("scales", scales);
constexpr int64_t N = 1, H = 2, W = 2, C = 2;
std::vector<float> X = {1.0f, 3.0f,
3.0f, 5.0f,
3.0f, 5.0f,
7.0f, 9.0f};
test.AddInput<float>("X", {N, H, W, C}, X);
std::vector<float> Y = {
1.0f, 3.0f,
1.0f, 3.0f,
1.0f, 3.0f,
3.0f, 5.0f,
3.0f, 5.0f,
3.0f, 5.0f,
1.0f, 3.0f,
1.0f, 3.0f,
1.0f, 3.0f,
3.0f, 5.0f,
3.0f, 5.0f,
3.0f, 5.0f,
3.0f, 5.0f,
3.0f, 5.0f,
3.0f, 5.0f,
7.0f, 9.0f,
7.0f, 9.0f,
7.0f, 9.0f,
3.0f, 5.0f,
3.0f, 5.0f,
3.0f, 5.0f,
7.0f, 9.0f,
7.0f, 9.0f,
7.0f, 9.0f};
test.AddOutput<float>("Y", {N, (int64_t)(H * scales[1]), (int64_t)(W * scales[2]), C}, Y);
//CUDA: result mismatch due to not implementing NHWC support
//TensorRT: results mismatch
//ROCm: results mismatch
test.Run(OpTester::ExpectResult::kExpectSuccess, "",
{kCudaExecutionProvider, kTensorrtExecutionProvider, kRocmExecutionProvider});
}
TEST(UpsampleOpTest, UpsampleOpNearestTest_int32) {
OpTester test("Upsample");
@ -73,6 +126,59 @@ TEST(UpsampleOpTest, UpsampleOpNearestTest_int32) {
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kTensorrtExecutionProvider}); //TensorRT: nvinfer1::query::Ports<nvinfer1::query::AbstractTensor>&): Assertion `!formats.empty()' failed
}
TEST(UpsampleOpTest, NhwcUpsampleOpNearestTest_int32) {
OpTester test("Upsample");
std::vector<float> scales{1.0f, 2.0f, 3.0f, 1.0f};
test.AddAttribute("mode", "nearest");
test.AddAttribute("scales", scales);
constexpr int64_t N = 1, H = 2, W = 2, C = 2;
std::vector<int32_t> X = {1, 3,
3, 5,
3, 5,
7, 9};
test.AddInput<int32_t>("X", {N, H, W, C}, X);
std::vector<int32_t> Y = {
1, 3,
1, 3,
1, 3,
3, 5,
3, 5,
3, 5,
1, 3,
1, 3,
1, 3,
3, 5,
3, 5,
3, 5,
3, 5,
3, 5,
3, 5,
7, 9,
7, 9,
7, 9,
3, 5,
3, 5,
3, 5,
7, 9,
7, 9,
7, 9};
test.AddOutput<int32_t>("Y", {N, (int64_t)(H * scales[1]), (int64_t)(W * scales[2]), C}, Y);
//CUDA: result mismatch due to not implementing NHWC support
//TensorRT: results mismatch
//ROCm: results mismatch
test.Run(OpTester::ExpectResult::kExpectSuccess, "",
{kCudaExecutionProvider, kTensorrtExecutionProvider, kRocmExecutionProvider});
}
TEST(UpsampleOpTest, UpsampleOpNearestTest_uint8) {
OpTester test("Upsample");
@ -104,6 +210,59 @@ TEST(UpsampleOpTest, UpsampleOpNearestTest_uint8) {
test.Run();
}
TEST(UpsampleOpTest, NhwcUpsampleOpNearestTest_uint8) {
OpTester test("Upsample");
std::vector<float> scales{1.0f, 2.0f, 3.0f, 1.0f};
test.AddAttribute("mode", "nearest");
test.AddAttribute("scales", scales);
constexpr int64_t N = 1, H = 2, W = 2, C = 2;
std::vector<uint8_t> X = {1, 3,
3, 5,
3, 5,
7, 9};
test.AddInput<uint8_t>("X", {N, H, W, C}, X);
std::vector<uint8_t> Y = {
1, 3,
1, 3,
1, 3,
3, 5,
3, 5,
3, 5,
1, 3,
1, 3,
1, 3,
3, 5,
3, 5,
3, 5,
3, 5,
3, 5,
3, 5,
7, 9,
7, 9,
7, 9,
3, 5,
3, 5,
3, 5,
7, 9,
7, 9,
7, 9};
test.AddOutput<uint8_t>("Y", {N, (int64_t)(H * scales[1]), (int64_t)(W * scales[2]), C}, Y);
//CUDA: result mismatch due to not implementing NHWC support
//TensorRT: results mismatch
//ROCm: results mismatch
test.Run(OpTester::ExpectResult::kExpectSuccess, "",
{kCudaExecutionProvider, kTensorrtExecutionProvider, kRocmExecutionProvider});
}
TEST(UpsampleOpTest, UpsampleOpNearest2XTest) {
OpTester test("Upsample");
@ -135,6 +294,51 @@ TEST(UpsampleOpTest, UpsampleOpNearest2XTest) {
test.Run();
}
TEST(UpsampleOpTest, NhwcUpsampleOpNearest2XTest) {
OpTester test("Upsample");
std::vector<float> scales{1.0f, 2.0f, 2.0f, 1.0f};
test.AddAttribute("mode", "nearest");
test.AddAttribute("scales", scales);
constexpr int64_t N = 1, H = 2, W = 2, C = 2;
std::vector<float> X = {1.0f, 3.0f,
3.0f, 5.0f,
3.0f, 5.0f,
7.0f, 9.0f};
test.AddInput<float>("X", {N, H, W, C}, X);
std::vector<float> Y = {
1.0f, 3.0f,
1.0f, 3.0f,
3.0f, 5.0f,
3.0f, 5.0f,
1.0f, 3.0f,
1.0f, 3.0f,
3.0f, 5.0f,
3.0f, 5.0f,
3.0f, 5.0f,
3.0f, 5.0f,
7.0f, 9.0f,
7.0f, 9.0f,
3.0f, 5.0f,
3.0f, 5.0f,
7.0f, 9.0f,
7.0f, 9.0f};
test.AddOutput<float>("Y", {N, (int64_t)(H * scales[1]), (int64_t)(W * scales[2]), C}, Y);
//CUDA: result mismatch due to not implementing NHWC support
//TensorRT: results mismatch
//ROCm: results mismatch
test.Run(OpTester::ExpectResult::kExpectSuccess, "",
{kCudaExecutionProvider, kTensorrtExecutionProvider, kRocmExecutionProvider});
}
TEST(UpsampleOpTest, UpsampleOpNearest222XTest) {
OpTester test("Upsample");
@ -176,6 +380,71 @@ TEST(UpsampleOpTest, UpsampleOpNearest222XTest) {
test.Run();
}
TEST(UpsampleOpTest, NhwcUpsampleOpNearest222XTest) {
OpTester test("Upsample");
std::vector<float> scales{2.0f, 2.0f, 2.0f, 1.0f};
test.AddAttribute("mode", "nearest");
test.AddAttribute("scales", scales);
constexpr int64_t N = 1, H = 2, W = 2, C = 2;
std::vector<float> X = {1.0f, 3.0f,
3.0f, 5.0f,
3.0f, 5.0f,
7.0f, 9.0f};
test.AddInput<float>("X", {N, H, W, C}, X);
std::vector<float> Y = {
1.0f, 3.0f,
1.0f, 3.0f,
3.0f, 5.0f,
3.0f, 5.0f,
1.0f, 3.0f,
1.0f, 3.0f,
3.0f, 5.0f,
3.0f, 5.0f,
3.0f, 5.0f,
3.0f, 5.0f,
7.0f, 9.0f,
7.0f, 9.0f,
3.0f, 5.0f,
3.0f, 5.0f,
7.0f, 9.0f,
7.0f, 9.0f,
1.0f, 3.0f,
1.0f, 3.0f,
3.0f, 5.0f,
3.0f, 5.0f,
1.0f, 3.0f,
1.0f, 3.0f,
3.0f, 5.0f,
3.0f, 5.0f,
3.0f, 5.0f,
3.0f, 5.0f,
7.0f, 9.0f,
7.0f, 9.0f,
3.0f, 5.0f,
3.0f, 5.0f,
7.0f, 9.0f,
7.0f, 9.0f};
test.AddOutput<float>("Y", {(int64_t)(N * scales[0]), (int64_t)(H * scales[1]), (int64_t)(W * scales[2]), C}, Y);
//CUDA: result mismatch due to not implementing NHWC support
//TensorRT: results mismatch
//ROCm: results mismatch
test.Run(OpTester::ExpectResult::kExpectSuccess, "",
{kCudaExecutionProvider, kTensorrtExecutionProvider, kRocmExecutionProvider});
}
TEST(UpsampleOpTest, UpsampleOpNearest15XTest) {
OpTester test("Upsample");
@ -207,6 +476,47 @@ TEST(UpsampleOpTest, UpsampleOpNearest15XTest) {
test.Run();
}
TEST(UpsampleOpTest, NhwcUpsampleOpNearest15XTest) {
OpTester test("Upsample");
std::vector<float> scales{1.0f, 2.0f, 1.5f, 1.0f};
test.AddAttribute("mode", "nearest");
test.AddAttribute("scales", scales);
constexpr int64_t N = 1, H = 2, W = 2, C = 2;
std::vector<float> X = {1.0f, 3.0f,
3.0f, 5.0f,
3.0f, 5.0f,
7.0f, 9.0f};
test.AddInput<float>("X", {N, H, W, C}, X);
std::vector<float> Y = {
1.0f, 3.0f,
1.0f, 3.0f,
3.0f, 5.0f,
1.0f, 3.0f,
1.0f, 3.0f,
3.0f, 5.0f,
3.0f, 5.0f,
3.0f, 5.0f,
7.0f, 9.0f,
3.0f, 5.0f,
3.0f, 5.0f,
7.0f, 9.0f};
test.AddOutput<float>("Y", {N, (int64_t)(H * scales[1]), (int64_t)(W * scales[2]), C}, Y);
//CUDA: result mismatch due to not implementing NHWC support
//TensorRT: results mismatch
//ROCm: results mismatch
test.Run(OpTester::ExpectResult::kExpectSuccess, "",
{kCudaExecutionProvider, kTensorrtExecutionProvider, kRocmExecutionProvider});
}
TEST(UpsampleOpTest, UpsampleOpNearestTest_NoScale) {
OpTester test("Upsample");
@ -264,6 +574,51 @@ TEST(UpsampleOpTest, UpsampleOpNearest2XTest_int32) {
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kTensorrtExecutionProvider}); //TensorRT: nvinfer1::query::Ports<nvinfer1::query::AbstractTensor>&): Assertion `!formats.empty()' failed
}
TEST(UpsampleOpTest, NhwcUpsampleOpNearest2XTest_int32) {
OpTester test("Upsample");
std::vector<float> scales{1.0f, 2.0f, 2.0f, 1.0f};
test.AddAttribute("mode", "nearest");
test.AddAttribute("scales", scales);
constexpr int64_t N = 1, H = 2, W = 2, C = 2;
std::vector<int32_t> X = {1, 3,
3, 5,
3, 5,
7, 9};
test.AddInput<int32_t>("X", {N, H, W, C}, X);
std::vector<int32_t> Y = {
1, 3,
1, 3,
3, 5,
3, 5,
1, 3,
1, 3,
3, 5,
3, 5,
3, 5,
3, 5,
7, 9,
7, 9,
3, 5,
3, 5,
7, 9,
7, 9};
test.AddOutput<int32_t>("Y", {N, (int64_t)(H * scales[1]), (int64_t)(W * scales[2]), C}, Y);
//CUDA: result mismatch due to not implementing NHWC support
//TensorRT: results mismatch
//ROCm: results mismatch
test.Run(OpTester::ExpectResult::kExpectSuccess, "",
{kCudaExecutionProvider, kTensorrtExecutionProvider, kRocmExecutionProvider});
}
TEST(UpsampleOpTest, UpsampleOp4DBilinearTest) {
OpTester test("Upsample");
@ -292,7 +647,111 @@ TEST(UpsampleOpTest, UpsampleOp4DBilinearTest) {
7.0f, 7.5f, 8.0f, 8.5f, 9.0f, 9.0f, 9.0f, 9.0f};
test.AddOutput<float>("Y", {N, C, (int64_t)(H * scales[2]), (int64_t)(W * scales[3])}, Y);
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kTensorrtExecutionProvider}); //TensorRT: results mismatch
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kTensorrtExecutionProvider}); //TensorRT: results mismatch
}
TEST(UpsampleOpTest, NhwcUpsampleOp4D1CBilinearTest) {
OpTester test("Upsample");
std::vector<float> scales{1.0f, 2.0f, 4.0f, 1.0f};
test.AddAttribute("mode", "linear");
test.AddAttribute("scales", scales);
constexpr int64_t N = 2, H = 2, W = 3, C = 1;
std::vector<float> X = {1.0f, 2.0f, 3.0f,
4.0f, 5.0f, 6.0f,
7.0f, 8.0f, 9.0f,
10.0f, 11.0f, 12.0f};
test.AddInput<float>("X", {N, H, W, C}, X);
std::vector<float> Y = {
1.0f, 1.25f, 1.5f, 1.75f, 2.0f, 2.25f, 2.5f, 2.75f, 3.0f, 3.0f, 3.0f, 3.0f,
2.5f, 2.75f, 3.0f, 3.25f, 3.5f, 3.75f, 4.0f, 4.25f, 4.5f, 4.5f, 4.5f, 4.5f,
4.0f, 4.25f, 4.5f, 4.75f, 5.0f, 5.25f, 5.5f, 5.75f, 6.0f, 6.0f, 6.0f, 6.0f,
4.0f, 4.25f, 4.5f, 4.75f, 5.0f, 5.25f, 5.5f, 5.75f, 6.0f, 6.0f, 6.0f, 6.0f,
7.0f, 7.25f, 7.5f, 7.75f, 8.0f, 8.25f, 8.5f, 8.75f, 9.0f, 9.0f, 9.0f, 9.0f,
8.5f, 8.75f, 9.0f, 9.25f, 9.5f, 9.75f, 10.0f, 10.25f, 10.5f, 10.5f, 10.5f, 10.5f,
10.0f, 10.25f, 10.5f, 10.75f, 11.0f, 11.25f, 11.5f, 11.75f, 12.0f, 12.0f, 12.0f, 12.0f,
10.0f, 10.25f, 10.5f, 10.75f, 11.0f, 11.25f, 11.5f, 11.75f, 12.0f, 12.0f, 12.0f, 12.0f};
test.AddOutput<float>("Y", {N, (int64_t)(H * scales[1]), (int64_t)(W * scales[2]), C}, Y);
//CUDA: result mismatch due to not implementing NHWC support
//TensorRT: results mismatch
//ROCm: results mismatch
test.Run(OpTester::ExpectResult::kExpectSuccess, "",
{kCudaExecutionProvider, kTensorrtExecutionProvider, kRocmExecutionProvider});
}
TEST(UpsampleOpTest, NhwcUpsampleOp4DBilinearTest) {
OpTester test("Upsample");
std::vector<float> scales{1.0f, 2.0f, 2.0f, 1.0f};
test.AddAttribute("mode", "linear");
test.AddAttribute("scales", scales);
constexpr int64_t N = 2, H = 2, W = 2, C = 3;
std::vector<float> X = {1.0f, 2.0f, 3.0f,
4.0f, 5.0f, 6.0f,
7.0f, 8.0f, 9.0f,
10.0f, 11.0f, 12.0f,
13.0f, 14.0f, 15.0f,
16.0f, 17.0f, 18.0f,
19.0f, 20.0f, 21.0f,
22.0f, 23.0f, 24.0f};
test.AddInput<float>("X", {N, H, W, C}, X);
std::vector<float> Y = {
1.0f, 2.0f, 3.0f,
2.5f, 3.5f, 4.5f,
4.0f, 5.0f, 6.0f,
4.0f, 5.0f, 6.0f,
4.0f, 5.0f, 6.0f,
5.5f, 6.5f, 7.5f,
7.0f, 8.0f, 9.0f,
7.0f, 8.0f, 9.0f,
7.0f, 8.0f, 9.0f,
8.5f, 9.5f, 10.5f,
10.0f, 11.0f, 12.0f,
10.0f, 11.0f, 12.0f,
7.0f, 8.0f, 9.0f,
8.5f, 9.5f, 10.5f,
10.0f, 11.0f, 12.0f,
10.0f, 11.0f, 12.0f,
13.0f, 14.0f, 15.0f,
14.5f, 15.5f, 16.5f,
16.0f, 17.0f, 18.0f,
16.0f, 17.0f, 18.0f,
16.0f, 17.0f, 18.0f,
17.5f, 18.5f, 19.5f,
19.0f, 20.0f, 21.0f,
19.0f, 20.0f, 21.0f,
19.0f, 20.0f, 21.0f,
20.5f, 21.5f, 22.5f,
22.0f, 23.0f, 24.0f,
22.0f, 23.0f, 24.0f,
19.0f, 20.0f, 21.0f,
20.5f, 21.5f, 22.5f,
22.0f, 23.0f, 24.0f,
22.0f, 23.0f, 24.0f};
test.AddOutput<float>("Y", {N, (int64_t)(H * scales[1]), (int64_t)(W * scales[2]), C}, Y);
//CUDA: result mismatch due to not implementing NHWC support
//TensorRT: results mismatch
//ROCm: results mismatch
test.Run(OpTester::ExpectResult::kExpectSuccess, "",
{kCudaExecutionProvider, kTensorrtExecutionProvider, kRocmExecutionProvider});
}
TEST(UpsampleOpTest, UpsampleOp2DBilinearTest) {
@ -375,6 +834,41 @@ TEST(UpsampleOpTest, UpsampleOp4DBilinearTest_int32) {
test.Run();
}
TEST(UpsampleOpTest, NhwcUpsampleOp4DBilinearTest_int32) {
OpTester test("Upsample");
std::vector<float> scales{1.0f, 2.0f, 4.0f, 1.0f};
test.AddAttribute("mode", "linear");
test.AddAttribute("scales", scales);
constexpr int64_t N = 2, H = 2, W = 2, C = 1;
std::vector<int32_t> X = {1, 3,
3, 5,
3, 5,
7, 9};
test.AddInput<int32_t>("X", {N, H, W, C}, X);
std::vector<int32_t> Y = {
1, 1, 2, 2, 3, 3, 3, 3,
2, 2, 3, 3, 4, 4, 4, 4,
3, 3, 4, 4, 5, 5, 5, 5,
3, 3, 4, 4, 5, 5, 5, 5,
3, 3, 4, 4, 5, 5, 5, 5,
5, 5, 6, 6, 7, 7, 7, 7,
7, 7, 8, 8, 9, 9, 9, 9,
7, 7, 8, 8, 9, 9, 9, 9};
test.AddOutput<int32_t>("Y", {N, (int64_t)(H * scales[1]), (int64_t)(W * scales[2]), C}, Y);
//CUDA: result mismatch due to not implementing NHWC support
//TensorRT: results mismatch
//ROCm: results mismatch
test.Run(OpTester::ExpectResult::kExpectSuccess, "",
{kCudaExecutionProvider, kTensorrtExecutionProvider, kRocmExecutionProvider});
}
TEST(UpsampleOpTest, UpsampleOpNearestTest_1D) {
OpTester test("Upsample");
@ -427,5 +921,50 @@ TEST(UpsampleOpTest, UpsampleOpNearest2XTest_opset9) {
test.AddOutput<int32_t>("Y", {N, C, (int64_t)(H * scales[2]), (int64_t)(W * scales[3])}, Y);
test.Run();
}
TEST(UpsampleOpTest, NhwcUpsampleOpNearest2XTest_opset9) {
OpTester test("Upsample", 9);
std::vector<float> scales{1.0f, 2.0f, 2.0f, 1.0};
test.AddAttribute("mode", "nearest");
constexpr int64_t N = 1, H = 2, W = 2, C = 2;
std::vector<int32_t> X = {1, 3,
3, 5,
3, 5,
7, 9};
test.AddInput<int32_t>("X", {N, H, W, C}, X);
test.AddInput<float>("scales", {4}, scales);
std::vector<int32_t> Y = {
1, 3,
1, 3,
3, 5,
3, 5,
1, 3,
1, 3,
3, 5,
3, 5,
3, 5,
3, 5,
7, 9,
7, 9,
3, 5,
3, 5,
7, 9,
7, 9};
test.AddOutput<int32_t>("Y", {N, (int64_t)(H * scales[1]), (int64_t)(W * scales[2]), C}, Y);
//CUDA: result mismatch due to not implementing NHWC support
//TensorRT: results mismatch
//ROCm: results mismatch
test.Run(OpTester::ExpectResult::kExpectSuccess, "",
{kCudaExecutionProvider, kTensorrtExecutionProvider, kRocmExecutionProvider});
}
} // namespace test
} // namespace onnxruntime