Add Features to ShortGrainDropper for ONNX export (#3628)

* add features to short_grain_dropper for ONNX export

* update FeaturizersLibrary

* fix warnings
This commit is contained in:
Ye Wang 2020-04-22 14:09:39 -07:00 committed by GitHub
parent 70b554cc85
commit 7837c7efc3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 167 additions and 33 deletions

View file

@ -1748,14 +1748,28 @@ void RegisterShortGrainDropperFeaturizerVer1() {
"T0")
.Input(
1,
"Input",
"String tensor of shape [R][K].",
"GrainInput",
"String tensor of shape [R][K]. Grain-related tensor",
"T1")
.Input(
2,
"Input",
"Variadic number of Input containing tensors of different size. Non-Grain-related tensors.",
"T",
ONNX_NAMESPACE::OpSchema::FormalParameterOption::Variadic,
false)
.Output(
0,
"GrainOutput",
"String tensor of shape [P][K], P <= R. Grain-related tensor after imputed(dropped)",
"T1")
.Output(
1,
"Output",
"Bool tensor of shape [R]",
"T2")
"Variadic number of Input containing tensors of different size. Non-Grain-related tensors after imputed(dropped).",
"T",
ONNX_NAMESPACE::OpSchema::FormalParameterOption::Variadic,
false)
.TypeConstraint(
"T0",
{"tensor(uint8)"},
@ -1765,21 +1779,29 @@ void RegisterShortGrainDropperFeaturizerVer1() {
{"tensor(string)"},
"No information is available")
.TypeConstraint(
"T2",
{"tensor(bool)"},
"T",
{"tensor(int8)", "tensor(int16)", "tensor(int32)", "tensor(int64)", "tensor(uint8)", "tensor(uint16)", "tensor(uint32)", "tensor(uint64)",
"tensor(float)", "tensor(double)", "tensor(bool)", "tensor(string)"},
"No information is available")
.TypeAndShapeInferenceFunction(
[](ONNX_NAMESPACE::InferenceContext& ctx) {
propagateElemTypeFromDtypeToOutput(ctx, ONNX_NAMESPACE::TensorProto_DataType_BOOL, 0);
propagateElemTypeFromDtypeToOutput(ctx, ONNX_NAMESPACE::TensorProto_DataType_STRING, 0);
if (hasInputShape(ctx, 1)) {
const auto& input_shape = getInputShape(ctx, 1);
if (input_shape.dim_size() != 2) {
fail_shape_inference("Expecting Input1 to have 2 dimensions");
}
ONNX_NAMESPACE::TensorShapeProto shape;
*shape.add_dim() = input_shape.dim(0);
shape.add_dim();
*shape.add_dim() = input_shape.dim(1);
ONNX_NAMESPACE::updateOutputShape(ctx, 0, shape);
}
if (hasInputShape(ctx, 2)) {
const auto& input_shape = getInputShape(ctx, 2);
if (input_shape.dim_size() != 2) {
fail_shape_inference("Expecting Input2 to have 2 dimensions");
}
}
});
}

View file

@ -12,6 +12,22 @@ namespace NS = Microsoft::Featurizer;
namespace onnxruntime {
namespace featurizers {
template <typename T>
struct CopyNonDroppedColumnsImpl {
void operator()(const Tensor* variadic_input_tensor, Tensor* output_after_drop_tensor,
const std::vector<bool>& rows_to_drop, int64_t input_row_size) const {
const T* input_data(variadic_input_tensor->template Data<T>());
T* output_after_drop_data = output_after_drop_tensor->MutableData<T>();
for (int row_idx = 0; row_idx < static_cast<int>(rows_to_drop.size()); ++row_idx) {
if (!rows_to_drop[row_idx]) {
output_after_drop_data = std::copy(input_data, input_data + input_row_size, output_after_drop_data);
}
input_data += input_row_size;
}
}
};
void ShortGrainDropperTransformerImpl(OpKernelContext* ctx) {
// Create the transformer
Microsoft::Featurizer::Featurizers::ShortGrainDropperTransformer transformer(
@ -23,26 +39,61 @@ void ShortGrainDropperTransformerImpl(OpKernelContext* ctx) {
return Microsoft::Featurizer::Featurizers::ShortGrainDropperTransformer(archive);
}());
// Get the input
// Get the Grain input
const auto* input_tensor = ctx->Input<Tensor>(1);
const std::string* input_data = input_tensor->template Data<std::string>();
// Prepare the output
const int64_t input_rows_num = input_tensor->Shape()[0];
const int64_t strings_num = input_tensor->Shape()[1];
TensorShape rows_shape({input_rows_num});
Tensor* output_tensor(ctx->Output(0, rows_shape));
bool* output_data(output_tensor->MutableData<bool>());
// Transform
ORT_ENFORCE(input_rows_num > 0, "input_rows_num > 0");
// Record which row to drop
std::vector<bool> rows_to_drop;
// Transform
std::vector<std::string> input_data_vec;
input_data_vec.reserve(strings_num);
for (int64_t rows_idx = 0; rows_idx < input_rows_num; ++rows_idx) {
input_data_vec.clear();
std::copy(input_data, input_data + strings_num, std::back_inserter(input_data_vec));
output_data[rows_idx] = transformer.execute(input_data_vec);
rows_to_drop.push_back(transformer.execute(input_data_vec));
input_data += strings_num;
}
// Calculate number of remaining rows
int remaining_rows_num = static_cast<int>(std::count(rows_to_drop.begin(), rows_to_drop.end(), false));
ORT_ENFORCE(remaining_rows_num > 0, "remaining_rows_num > 0");
// Prepare the Grain output
TensorShape grain_output_shape({remaining_rows_num, strings_num});
Tensor* grain_output_tensor(ctx->Output(0, grain_output_shape));
std::string* grain_output_data(grain_output_tensor->MutableData<std::string>());
const std::string* input_grain_data = input_tensor->template Data<std::string>();
for (int rows_idx = 0; rows_idx < static_cast<int>(input_rows_num); ++rows_idx) {
if (!rows_to_drop[rows_idx]) {
grain_output_data = std::copy(input_grain_data, input_grain_data + strings_num, grain_output_data);
}
input_grain_data += strings_num;
}
// Prepare other outputs. input(2)->output(1), input(3)->output(2), ...
const int variadic_input_start_id = ctx->NumVariadicInputs(0) + ctx->NumVariadicInputs(1);
const int variadic_input_end_id = variadic_input_start_id + ctx->NumVariadicInputs(2);
for (int input_id = variadic_input_start_id; input_id < variadic_input_end_id; ++input_id) {
const auto* variadic_input_tensor(ctx->Input<Tensor>(input_id)); //2-d tensor
const int64_t input_row_size = variadic_input_tensor->Shape()[1];
TensorShape output_after_drop_shape({static_cast<int64_t>(remaining_rows_num), input_row_size});
Tensor* output_after_drop_tensor(ctx->Output(input_id - 1, output_after_drop_shape));
const auto elem_type = variadic_input_tensor->GetElementType();
utils::MLTypeCallDispatcher<CopyNonDroppedColumnsImpl,
int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t,
float, double, bool, std::string> t_disp(elem_type);
t_disp.Invoke(variadic_input_tensor, output_after_drop_tensor, rows_to_drop, input_row_size);
}
};
class ShortGrainDropperTransformer final : public OpKernel {
@ -62,7 +113,19 @@ ONNX_OPERATOR_KERNEL_EX(
kCpuExecutionProvider,
KernelDefBuilder()
.TypeConstraint("T0", DataTypeImpl::GetTensorType<uint8_t>())
.TypeConstraint("T1", DataTypeImpl::GetTensorType<std::string>()),
.TypeConstraint("T1", DataTypeImpl::GetTensorType<std::string>())
.TypeConstraint("T", {DataTypeImpl::GetTensorType<int8_t>(),
DataTypeImpl::GetTensorType<uint8_t>(),
DataTypeImpl::GetTensorType<int16_t>(),
DataTypeImpl::GetTensorType<uint16_t>(),
DataTypeImpl::GetTensorType<int32_t>(),
DataTypeImpl::GetTensorType<uint32_t>(),
DataTypeImpl::GetTensorType<int64_t>(),
DataTypeImpl::GetTensorType<uint64_t>(),
DataTypeImpl::GetTensorType<float>(),
DataTypeImpl::GetTensorType<double>(),
DataTypeImpl::GetTensorType<bool>(),
DataTypeImpl::GetTensorType<std::string>()}),
ShortGrainDropperTransformer);
} // namespace featurizers

View file

@ -25,27 +25,27 @@ std::vector<uint8_t> GetStream(EstimatorT& estimator, const std::vector<std::vec
} // namespace
TEST(FeaturizersTests, ShortGrainDropperTransformer_Has_CV) {
TEST(FeaturizersTests, ShortGrainDropperTransformer_Min_4) {
EstimatorT estimator(NS::CreateTestAnnotationMapsPtr(1), 0, 4);
std::vector<std::vector<std::vector<std::string>>> trainingBatches = NS::TestHelpers::make_vector<std::vector<std::vector<std::string>>>(
NS::TestHelpers::make_vector<std::vector<std::string>>(
NS::TestHelpers::make_vector<std::string>("a", "b"), //false
NS::TestHelpers::make_vector<std::string>("a", "b"),
NS::TestHelpers::make_vector<std::string>("a", "b"),
NS::TestHelpers::make_vector<std::string>("a", "b"),
NS::TestHelpers::make_vector<std::string>("a", "b"),
NS::TestHelpers::make_vector<std::string>("a", "b"),
NS::TestHelpers::make_vector<std::string>("a", "c"),
NS::TestHelpers::make_vector<std::string>("a", "c"), //false
NS::TestHelpers::make_vector<std::string>("a", "c"),
NS::TestHelpers::make_vector<std::string>("a", "c"),
NS::TestHelpers::make_vector<std::string>("a", "c"),
NS::TestHelpers::make_vector<std::string>("a", "d"), //true
NS::TestHelpers::make_vector<std::string>("a", "d"),
NS::TestHelpers::make_vector<std::string>("a", "d"),
NS::TestHelpers::make_vector<std::string>("a", "d"),
NS::TestHelpers::make_vector<std::string>("a", "e"), //true
NS::TestHelpers::make_vector<std::string>("a", "e"),
NS::TestHelpers::make_vector<std::string>("a", "e"),
NS::TestHelpers::make_vector<std::string>("a", "f")
NS::TestHelpers::make_vector<std::string>("a", "f") //true
)
);
@ -53,33 +53,38 @@ TEST(FeaturizersTests, ShortGrainDropperTransformer_Has_CV) {
auto dim = static_cast<int64_t>(stream.size());
OpTester test("ShortGrainDropperTransformer", 1, onnxruntime::kMSFeaturizersDomain);
test.AddInput<uint8_t>("State", {dim}, stream);
test.AddInput<std::string>("Input", {6, 2}, {"a", "b", "a", "c", "a", "d", "a", "e", "a", "f", "a", "g"});
test.AddOutput<bool>("Output", {6}, {false, false, true, true, true, true});
test.AddInput<std::string>("GrainInput", {6, 2}, {"a", "b", "a", "c", "a", "d", "a", "e", "a", "f", "a", "g"});
test.AddInput<std::string>("Non_GrainInput_1", {6, 2}, {"c", "c", "d", "d", "e", "e", "e", "e", "e", "e", "e", "e"});
test.AddInput<double>("Non_GrainInput_2", {6, 1}, {1, 2, 3, 4, 5, 6});
test.AddOutput<std::string>("GrainOutput", {2, 2}, {"a", "b", "a", "c"});
test.AddOutput<std::string>("Non_GrainOutput_1", {2, 2}, {"c", "c", "d", "d"});
test.AddOutput<double>("Non_GrainOutput_2", {2, 1}, {1, 2});
test.Run();
}
TEST(FeaturizersTests, ShortGrainDropperTransformer_No_CV) {
TEST(FeaturizersTests, ShortGrainDropperTransformer_Min_3) {
EstimatorT estimator(NS::CreateTestAnnotationMapsPtr(1), 0, 3);
std::vector<std::vector<std::vector<std::string>>> trainingBatches = NS::TestHelpers::make_vector<std::vector<std::vector<std::string>>>(
NS::TestHelpers::make_vector<std::vector<std::string>>(
NS::TestHelpers::make_vector<std::string>("a", "b"), //false
NS::TestHelpers::make_vector<std::string>("a", "b"),
NS::TestHelpers::make_vector<std::string>("a", "b"),
NS::TestHelpers::make_vector<std::string>("a", "b"),
NS::TestHelpers::make_vector<std::string>("a", "b"),
NS::TestHelpers::make_vector<std::string>("a", "b"),
NS::TestHelpers::make_vector<std::string>("a", "c"),
NS::TestHelpers::make_vector<std::string>("a", "c"), //false
NS::TestHelpers::make_vector<std::string>("a", "c"),
NS::TestHelpers::make_vector<std::string>("a", "c"),
NS::TestHelpers::make_vector<std::string>("a", "c"),
NS::TestHelpers::make_vector<std::string>("a", "d"), //false
NS::TestHelpers::make_vector<std::string>("a", "d"),
NS::TestHelpers::make_vector<std::string>("a", "d"),
NS::TestHelpers::make_vector<std::string>("a", "d"),
NS::TestHelpers::make_vector<std::string>("a", "e"), //true
NS::TestHelpers::make_vector<std::string>("a", "e"),
NS::TestHelpers::make_vector<std::string>("a", "e"),
NS::TestHelpers::make_vector<std::string>("a", "f")
NS::TestHelpers::make_vector<std::string>("a", "f") //true
)
);
@ -87,8 +92,52 @@ TEST(FeaturizersTests, ShortGrainDropperTransformer_No_CV) {
auto dim = static_cast<int64_t>(stream.size());
OpTester test("ShortGrainDropperTransformer", 1, onnxruntime::kMSFeaturizersDomain);
test.AddInput<uint8_t>("State", {dim}, stream);
test.AddInput<std::string>("Input", {6, 2}, {"a", "b", "a", "c", "a", "d", "a", "e", "a", "f", "a", "g"});
test.AddOutput<bool>("Output", {6}, {false, false, false, true, true, true});
test.AddInput<std::string>("GrainInput", {6, 2}, {"a", "b", "a", "c", "a", "d", "a", "e", "a", "f", "a", "g"});
test.AddInput<std::string>("Non_GrainInput_1", {6, 2}, {"c", "c", "d", "d", "e", "e", "e", "e", "e", "e", "e", "e"});
test.AddInput<double>("Non_GrainInput_2", {6, 1}, {1, 2, 3, 4, 5, 6});
test.AddOutput<std::string>("GrainOutput", {3, 2}, {"a", "b", "a", "c", "a", "d"});
test.AddOutput<std::string>("Non_GrainOutput_1", {3, 2}, {"c", "c", "d", "d", "e", "e"});
test.AddOutput<double>("Non_GrainOutput_2", {3, 1}, {1, 2, 3});
test.Run();
}
TEST(FeaturizersTests, ShortGrainDropperTransformer_Min_2) {
EstimatorT estimator(NS::CreateTestAnnotationMapsPtr(1), 0, 2);
std::vector<std::vector<std::vector<std::string>>> trainingBatches = NS::TestHelpers::make_vector<std::vector<std::vector<std::string>>>(
NS::TestHelpers::make_vector<std::vector<std::string>>(
NS::TestHelpers::make_vector<std::string>("a", "b"), //false
NS::TestHelpers::make_vector<std::string>("a", "b"),
NS::TestHelpers::make_vector<std::string>("a", "b"),
NS::TestHelpers::make_vector<std::string>("a", "b"),
NS::TestHelpers::make_vector<std::string>("a", "b"),
NS::TestHelpers::make_vector<std::string>("a", "c"), //false
NS::TestHelpers::make_vector<std::string>("a", "c"),
NS::TestHelpers::make_vector<std::string>("a", "c"),
NS::TestHelpers::make_vector<std::string>("a", "c"),
NS::TestHelpers::make_vector<std::string>("a", "d"), //false
NS::TestHelpers::make_vector<std::string>("a", "d"),
NS::TestHelpers::make_vector<std::string>("a", "d"),
NS::TestHelpers::make_vector<std::string>("a", "e"), //false
NS::TestHelpers::make_vector<std::string>("a", "e"),
NS::TestHelpers::make_vector<std::string>("a", "f") //true
)
);
auto stream = GetStream(estimator, trainingBatches);
auto dim = static_cast<int64_t>(stream.size());
OpTester test("ShortGrainDropperTransformer", 1, onnxruntime::kMSFeaturizersDomain);
test.AddInput<uint8_t>("State", {dim}, stream);
test.AddInput<std::string>("GrainInput", {6, 2}, {"a", "b", "a", "c", "a", "d", "a", "e", "a", "f", "a", "g"});
test.AddInput<std::string>("Non_GrainInput_1", {6, 2}, {"c", "c", "d", "d", "e", "e", "e", "e", "e", "e", "e", "e"});
test.AddInput<double>("Non_GrainInput_2", {6, 1}, {1, 2, 3, 4, 5, 6});
test.AddOutput<std::string>("GrainOutput", {4, 2}, {"a", "b", "a", "c", "a", "d", "a", "e"});
test.AddOutput<std::string>("Non_GrainOutput_1", {4, 2}, {"c", "c", "d", "d", "e", "e", "e", "e"});
test.AddOutput<double>("Non_GrainOutput_2", {4, 1}, {1, 2, 3, 4});
test.Run();
}