Implement Sign operator. (#456)

Implement Sign operator.
This commit is contained in:
Dmitri Smirnov 2019-02-11 10:25:54 -08:00 committed by GitHub
parent 360fc32db4
commit aac711ab2f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 297 additions and 3 deletions

View file

@ -236,6 +236,7 @@ class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain,
class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 9, EyeLike);
class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 9, float, IsNaN);
class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 9, MLFloat16, IsNaN);
class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 9, Sign);
class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 9, Erf);
class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 9, int64_t_int64_t_int64_t, OneHot);
class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 9, float_int64_t_int64_t, OneHot);
@ -459,7 +460,7 @@ void RegisterOnnxOperatorKernels(KernelRegistry& kernel_registry) {
kernel_registry.Register(BuildKernelCreateInfo<ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 1, Unsqueeze)>());
kernel_registry.Register(BuildKernelCreateInfo<ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 7, 9, float, Upsample)>());
kernel_registry.Register(BuildKernelCreateInfo<ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 7, 9, int32_t, Upsample)>());
kernel_registry.Register(BuildKernelCreateInfo<ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 7, 9, uint8_t, Upsample)>());
kernel_registry.Register(BuildKernelCreateInfo<ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 7, 9, uint8_t, Upsample)>());
kernel_registry.Register(BuildKernelCreateInfo<ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 8, float, Expand)>());
kernel_registry.Register(BuildKernelCreateInfo<ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 8, double, Expand)>());
kernel_registry.Register(BuildKernelCreateInfo<ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 8, int8_t, Expand)>());
@ -486,6 +487,7 @@ void RegisterOnnxOperatorKernels(KernelRegistry& kernel_registry) {
kernel_registry.Register(BuildKernelCreateInfo<ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 9, EyeLike)>());
kernel_registry.Register(BuildKernelCreateInfo<ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 9, float, IsNaN)>());
kernel_registry.Register(BuildKernelCreateInfo<ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 9, MLFloat16, IsNaN)>());
kernel_registry.Register(BuildKernelCreateInfo<ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 9, Sign)>());
kernel_registry.Register(BuildKernelCreateInfo<ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 9, Erf)>());
kernel_registry.Register(BuildKernelCreateInfo<ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 9, int64_t_int64_t_int64_t, OneHot)>());
kernel_registry.Register(BuildKernelCreateInfo<ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 9, float_int64_t_int64_t, OneHot)>());

View file

@ -0,0 +1,112 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "core/common/common.h"
#include "core/framework/data_types.h"
#include "core/framework/op_kernel.h"
#include "core/util/math.h"
#include "core/util/math_cpuonly.h"
#include "gsl/span"
#include <type_traits>
using namespace ::onnxruntime::common;
using namespace ONNX_NAMESPACE;
namespace onnxruntime {
class Sign final : public OpKernel {
public:
explicit Sign(const OpKernelInfo& info) : OpKernel(info) {}
Status Compute(OpKernelContext* ctx) const override;
};
ONNX_CPU_OPERATOR_KERNEL(
Sign,
9,
KernelDefBuilder().TypeConstraint("T", {DataTypeImpl::GetTensorType<float>(),
DataTypeImpl::GetTensorType<double>(),
DataTypeImpl::GetTensorType<int64_t>(),
DataTypeImpl::GetTensorType<uint64_t>(),
DataTypeImpl::GetTensorType<int32_t>(),
DataTypeImpl::GetTensorType<uint32_t>(),
DataTypeImpl::GetTensorType<int16_t>(),
DataTypeImpl::GetTensorType<uint16_t>(),
DataTypeImpl::GetTensorType<int8_t>(),
DataTypeImpl::GetTensorType<uint8_t>(),
DataTypeImpl::GetTensorType<MLFloat16>(),
DataTypeImpl::GetTensorType<BFloat16>()}),
Sign);
namespace sign_internal {
// The spec does not specify how NaN is
// treated but we have to treat it somehow. We choose
// to return 0 for NaN as TF does.
template <class T>
inline T FloatingImpl(T val) {
if (std::isnan(val) || val == T(0)) {
return T(0);
} else if (val > T(0)) {
return T(1);
} else {
return T(-1);
}
}
void SignMLFloat16(const Tensor* input, Tensor* output) {
auto span = gsl::make_span(input->Data<MLFloat16>(), input->Shape().Size());
auto output_data = output->template MutableData<MLFloat16>();
std::transform(span.cbegin(), span.cend(), output_data, [](const MLFloat16& val) {
float fl = math::halfToFloat(val.val);
return MLFloat16(math::floatToHalf(FloatingImpl(fl)));
});
}
void SignBFloat16(const Tensor* input, Tensor* output) {
auto span = gsl::make_span(input->Data<BFloat16>(), input->Shape().Size());
auto output_data = output->template MutableData<BFloat16>();
std::transform(span.cbegin(), span.cend(), output_data, [](const BFloat16& val) {
float fl = val.ToFloat();
return BFloat16(FloatingImpl(fl));
});
}
} // namespace sign_internal
Status Sign::Compute(OpKernelContext* ctx) const {
using namespace sign_internal;
auto input = ctx->Input<Tensor>(0);
auto output = ctx->Output(0, input->Shape());
auto dtype = input->DataType();
if (dtype == DataTypeImpl::GetType<float>()) {
EigenMap<float>(*output) = EigenMap<float>(*input).array().cwiseSign();
} else if (dtype == DataTypeImpl::GetType<double>()) {
EigenMap<double>(*output) = EigenMap<double>(*input).array().cwiseSign();
} else if (dtype == DataTypeImpl::GetType<int8_t>()) {
EigenMap<int8_t>(*output) = EigenMap<int8_t>(*input).array().cwiseSign();
} else if (dtype == DataTypeImpl::GetType<int16_t>()) {
EigenMap<int16_t>(*output) = EigenMap<int16_t>(*input).array().cwiseSign();
} else if (dtype == DataTypeImpl::GetType<int32_t>()) {
EigenMap<int32_t>(*output) = EigenMap<int32_t>(*input).array().cwiseSign();
} else if (dtype == DataTypeImpl::GetType<int64_t>()) {
EigenMap<int64_t>(*output) = EigenMap<int64_t>(*input).array().cwiseSign();
} else if (dtype == DataTypeImpl::GetType<uint8_t>()) {
EigenMap<uint8_t>(*output) = EigenMap<uint8_t>(*input).array().cwiseSign();
} else if (dtype == DataTypeImpl::GetType<uint16_t>()) {
EigenMap<uint16_t>(*output) = EigenMap<uint16_t>(*input).array().cwiseSign();
} else if (dtype == DataTypeImpl::GetType<uint32_t>()) {
EigenMap<uint32_t>(*output) = EigenMap<uint32_t>(*input).array().cwiseSign();
} else if (dtype == DataTypeImpl::GetType<uint64_t>()) {
EigenMap<uint64_t>(*output) = EigenMap<uint64_t>(*input).array().cwiseSign();
} else if (dtype == DataTypeImpl::GetType<MLFloat16>()) {
SignMLFloat16(input, output);
} else if (dtype == DataTypeImpl::GetType<BFloat16>()) {
SignBFloat16(input, output);
} else {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, "Unsupported input datatype");
}
return Status::OK();
}
} // namespace onnxruntime

View file

@ -309,7 +309,6 @@ int real_main(int argc, char* argv[]) {
{"acosh_example", "opset 9 not supported yet"},
{"atanh_example", "opset 9 not supported yet"},
{"sign_model", "opset 9 not supported yet"},
{"sign", "opset 9 not supported yet"},
{"scatter_with_axis", "opset 9 not supported yet"},
{"scatter_without_axis", "opset 9 not supported yet"},
{"scan_sum", "opset 9 not supported yet"},

View file

@ -0,0 +1,182 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "gtest/gtest.h"
#include "test/providers/provider_test_utils.h"
#include "core/util/math.h"
namespace onnxruntime {
namespace test {
namespace test_sign_internal {
template <class T, class A>
struct make_type {
static T make(A v) {
return T(v);
}
};
template <class A>
struct make_type<MLFloat16, A> {
static MLFloat16 make(A v) {
return MLFloat16(math::floatToHalf(float(v)));
}
};
template <class A>
struct make_type<BFloat16, A> {
static BFloat16 make(A v) {
return BFloat16(float(v));
}
};
template <class T, class OutputIter>
typename std::enable_if<!std::numeric_limits<T>::is_signed>::type
GenerateSequence(OutputIter out) {
for (int i = 0; i < 7; ++i) {
*out = make_type<T, int>::make(i);
++out;
}
}
template <class T, class OutputIter>
typename std::enable_if<std::numeric_limits<T>::is_signed>::type
GenerateSequence(OutputIter out) {
for (int i = -5; i < 2; ++i) {
*out = make_type<T, int>::make(i);
++out;
}
}
template <class T>
inline auto to_testable_type(T v) {
return v;
}
template <>
inline auto to_testable_type(MLFloat16 v) {
return math::halfToFloat(v.val);
}
template <>
inline auto to_testable_type(BFloat16 v) {
return v.ToFloat();
}
template <class T, class ForwardIter, class OutputIter>
typename std::enable_if<!std::numeric_limits<T>::is_signed &&
!std::is_same<T, MLFloat16>::value &&
!std::is_same<T, BFloat16>::value>::type
TestImpl(ForwardIter first, ForwardIter last, OutputIter out) {
std::transform(first, last, out, [](T v) {
auto t = to_testable_type<T>(v);
if (t == 0) {
t = 0;
} else {
t = 1;
}
return make_type<T, decltype(t)>::make(t);
});
}
template <class T, class ForwardIter, class OutputIter>
typename std::enable_if<std::numeric_limits<T>::is_signed ||
std::is_same<T, MLFloat16>::value ||
std::is_same<T, BFloat16>::value>::type
TestImpl(ForwardIter first, ForwardIter last, OutputIter out) {
std::transform(first, last, out, [](T v) {
auto t = to_testable_type<T>(v);
if (t == 0) {
t = 0;
} else if (t > 0) {
t = 1;
} else {
t = -1;
}
return make_type<T, decltype(t)>::make(t);
});
}
} // namespace test_sign_internal
TEST(MathOpTest, Sign_uint64) {
using namespace test_sign_internal;
OpTester test("Sign", 9);
std::vector<int64_t> input_dims{7};
std::vector<uint64_t> input;
GenerateSequence<uint64_t>(std::back_inserter(input));
ASSERT_EQ(input.size(), 7U);
test.AddInput<uint64_t>("input", input_dims, input);
std::vector<uint64_t> output;
TestImpl<uint64_t>(input.cbegin(), input.cend(), std::back_inserter(output));
test.AddOutput<uint64_t>("output", input_dims, output);
test.Run(OpTester::ExpectResult::kExpectSuccess);
}
TEST(MathOpTest, Sign_int64) {
using namespace test_sign_internal;
OpTester test("Sign", 9);
std::vector<int64_t> input_dims{7};
std::vector<int64_t> input;
GenerateSequence<int64_t>(std::back_inserter(input));
ASSERT_EQ(input.size(), 7U);
test.AddInput<int64_t>("input", input_dims, input);
std::vector<int64_t> output;
TestImpl<int64_t>(input.cbegin(), input.cend(), std::back_inserter(output));
test.AddOutput<int64_t>("output", input_dims, output);
test.Run(OpTester::ExpectResult::kExpectSuccess);
}
TEST(MathOpTest, Sign_float) {
using namespace test_sign_internal;
OpTester test("Sign", 9);
std::vector<int64_t> input_dims{7};
std::vector<float> input;
GenerateSequence<float>(std::back_inserter(input));
ASSERT_EQ(input.size(), 7U);
test.AddInput<float>("input", input_dims, input);
std::vector<float> output;
TestImpl<float>(input.cbegin(), input.cend(), std::back_inserter(output));
test.AddOutput<float>("output", input_dims, output);
test.Run(OpTester::ExpectResult::kExpectSuccess);
}
TEST(MathOpTest, Sign_double) {
using namespace test_sign_internal;
OpTester test("Sign", 9);
std::vector<int64_t> input_dims{7};
std::vector<double> input;
GenerateSequence<double>(std::back_inserter(input));
ASSERT_EQ(input.size(), 7U);
test.AddInput<double>("input", input_dims, input);
std::vector<double> output;
TestImpl<double>(input.cbegin(), input.cend(), std::back_inserter(output));
test.AddOutput<double>("output", input_dims, output);
test.Run(OpTester::ExpectResult::kExpectSuccess);
}
TEST(MathOpTest, Sign_MLFloat16) {
using namespace test_sign_internal;
OpTester test("Sign", 9);
std::vector<int64_t> input_dims{7};
std::vector<MLFloat16> input;
GenerateSequence<MLFloat16>(std::back_inserter(input));
ASSERT_EQ(input.size(), 7U);
test.AddInput<MLFloat16>("input", input_dims, input);
std::vector<MLFloat16> output;
TestImpl<MLFloat16>(input.cbegin(), input.cend(), std::back_inserter(output));
test.AddOutput<MLFloat16>("output", input_dims, output);
test.Run(OpTester::ExpectResult::kExpectSuccess);
}
} // namespace test
} // namespace onnxruntime

View file

@ -31,7 +31,6 @@ backend_test.exclude(r'('
'|^test_scatter_without_axis_cpu.*'
'|^test_shrink_hard_cpu.*'
'|^test_shrink_soft_cpu.*'
'|^test_sign_cpu.*'
'|^test_where_example_cpu.*'
'|^test_AvgPool1d_cpu.*'
'|^test_AvgPool1d_stride_cpu.*'