From 7837c7efc358d2c492c47c2d0fe7421474b3abe4 Mon Sep 17 00:00:00 2001 From: Ye Wang <52801275+wangyems@users.noreply.github.com> Date: Wed, 22 Apr 2020 14:09:39 -0700 Subject: [PATCH] Add Features to ShortGrainDropper for ONNX export (#3628) * add features to short_grain_dropper for ONNX export * update FeaturizersLibrary * fix warnings --- .../graph/featurizers_ops/featurizers_defs.cc | 38 +++++++-- .../cpu/short_grain_dropper_transformer.cc | 81 ++++++++++++++++--- .../short_grain_dropper_transformer_test.cc | 81 +++++++++++++++---- 3 files changed, 167 insertions(+), 33 deletions(-) diff --git a/onnxruntime/core/graph/featurizers_ops/featurizers_defs.cc b/onnxruntime/core/graph/featurizers_ops/featurizers_defs.cc index 6c9046da7a..2878094d8d 100644 --- a/onnxruntime/core/graph/featurizers_ops/featurizers_defs.cc +++ b/onnxruntime/core/graph/featurizers_ops/featurizers_defs.cc @@ -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"); + } + } }); } diff --git a/onnxruntime/featurizers_ops/cpu/short_grain_dropper_transformer.cc b/onnxruntime/featurizers_ops/cpu/short_grain_dropper_transformer.cc index 7cfc34fad2..6a2eb233d3 100644 --- a/onnxruntime/featurizers_ops/cpu/short_grain_dropper_transformer.cc +++ b/onnxruntime/featurizers_ops/cpu/short_grain_dropper_transformer.cc @@ -12,6 +12,22 @@ namespace NS = Microsoft::Featurizer; namespace onnxruntime { namespace featurizers { +template +struct CopyNonDroppedColumnsImpl { + void operator()(const Tensor* variadic_input_tensor, Tensor* output_after_drop_tensor, + const std::vector& rows_to_drop, int64_t input_row_size) const { + const T* input_data(variadic_input_tensor->template Data()); + T* output_after_drop_data = output_after_drop_tensor->MutableData(); + + for (int row_idx = 0; row_idx < static_cast(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(1); const std::string* input_data = input_tensor->template Data(); - - // 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()); - // Transform + ORT_ENFORCE(input_rows_num > 0, "input_rows_num > 0"); + + // Record which row to drop + std::vector rows_to_drop; + // Transform std::vector 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(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()); + const std::string* input_grain_data = input_tensor->template Data(); + for (int rows_idx = 0; rows_idx < static_cast(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(input_id)); //2-d tensor + const int64_t input_row_size = variadic_input_tensor->Shape()[1]; + + TensorShape output_after_drop_shape({static_cast(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 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()) - .TypeConstraint("T1", DataTypeImpl::GetTensorType()), + .TypeConstraint("T1", DataTypeImpl::GetTensorType()) + .TypeConstraint("T", {DataTypeImpl::GetTensorType(), + DataTypeImpl::GetTensorType(), + DataTypeImpl::GetTensorType(), + DataTypeImpl::GetTensorType(), + DataTypeImpl::GetTensorType(), + DataTypeImpl::GetTensorType(), + DataTypeImpl::GetTensorType(), + DataTypeImpl::GetTensorType(), + DataTypeImpl::GetTensorType(), + DataTypeImpl::GetTensorType(), + DataTypeImpl::GetTensorType(), + DataTypeImpl::GetTensorType()}), ShortGrainDropperTransformer); } // namespace featurizers diff --git a/onnxruntime/test/featurizers_ops/short_grain_dropper_transformer_test.cc b/onnxruntime/test/featurizers_ops/short_grain_dropper_transformer_test.cc index fabd25ac91..2b916c22c9 100644 --- a/onnxruntime/test/featurizers_ops/short_grain_dropper_transformer_test.cc +++ b/onnxruntime/test/featurizers_ops/short_grain_dropper_transformer_test.cc @@ -25,27 +25,27 @@ std::vector GetStream(EstimatorT& estimator, const std::vector>> trainingBatches = NS::TestHelpers::make_vector>>( NS::TestHelpers::make_vector>( + NS::TestHelpers::make_vector("a", "b"), //false NS::TestHelpers::make_vector("a", "b"), NS::TestHelpers::make_vector("a", "b"), NS::TestHelpers::make_vector("a", "b"), NS::TestHelpers::make_vector("a", "b"), - NS::TestHelpers::make_vector("a", "b"), - NS::TestHelpers::make_vector("a", "c"), + NS::TestHelpers::make_vector("a", "c"), //false NS::TestHelpers::make_vector("a", "c"), NS::TestHelpers::make_vector("a", "c"), NS::TestHelpers::make_vector("a", "c"), + NS::TestHelpers::make_vector("a", "d"), //true NS::TestHelpers::make_vector("a", "d"), NS::TestHelpers::make_vector("a", "d"), - NS::TestHelpers::make_vector("a", "d"), + NS::TestHelpers::make_vector("a", "e"), //true NS::TestHelpers::make_vector("a", "e"), - NS::TestHelpers::make_vector("a", "e"), - NS::TestHelpers::make_vector("a", "f") + NS::TestHelpers::make_vector("a", "f") //true ) ); @@ -53,33 +53,38 @@ TEST(FeaturizersTests, ShortGrainDropperTransformer_Has_CV) { auto dim = static_cast(stream.size()); OpTester test("ShortGrainDropperTransformer", 1, onnxruntime::kMSFeaturizersDomain); test.AddInput("State", {dim}, stream); - test.AddInput("Input", {6, 2}, {"a", "b", "a", "c", "a", "d", "a", "e", "a", "f", "a", "g"}); - test.AddOutput("Output", {6}, {false, false, true, true, true, true}); + test.AddInput("GrainInput", {6, 2}, {"a", "b", "a", "c", "a", "d", "a", "e", "a", "f", "a", "g"}); + test.AddInput("Non_GrainInput_1", {6, 2}, {"c", "c", "d", "d", "e", "e", "e", "e", "e", "e", "e", "e"}); + test.AddInput("Non_GrainInput_2", {6, 1}, {1, 2, 3, 4, 5, 6}); + + test.AddOutput("GrainOutput", {2, 2}, {"a", "b", "a", "c"}); + test.AddOutput("Non_GrainOutput_1", {2, 2}, {"c", "c", "d", "d"}); + test.AddOutput("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>> trainingBatches = NS::TestHelpers::make_vector>>( NS::TestHelpers::make_vector>( + NS::TestHelpers::make_vector("a", "b"), //false NS::TestHelpers::make_vector("a", "b"), NS::TestHelpers::make_vector("a", "b"), NS::TestHelpers::make_vector("a", "b"), NS::TestHelpers::make_vector("a", "b"), - NS::TestHelpers::make_vector("a", "b"), - NS::TestHelpers::make_vector("a", "c"), + NS::TestHelpers::make_vector("a", "c"), //false NS::TestHelpers::make_vector("a", "c"), NS::TestHelpers::make_vector("a", "c"), NS::TestHelpers::make_vector("a", "c"), + NS::TestHelpers::make_vector("a", "d"), //false NS::TestHelpers::make_vector("a", "d"), NS::TestHelpers::make_vector("a", "d"), - NS::TestHelpers::make_vector("a", "d"), + NS::TestHelpers::make_vector("a", "e"), //true NS::TestHelpers::make_vector("a", "e"), - NS::TestHelpers::make_vector("a", "e"), - NS::TestHelpers::make_vector("a", "f") + NS::TestHelpers::make_vector("a", "f") //true ) ); @@ -87,8 +92,52 @@ TEST(FeaturizersTests, ShortGrainDropperTransformer_No_CV) { auto dim = static_cast(stream.size()); OpTester test("ShortGrainDropperTransformer", 1, onnxruntime::kMSFeaturizersDomain); test.AddInput("State", {dim}, stream); - test.AddInput("Input", {6, 2}, {"a", "b", "a", "c", "a", "d", "a", "e", "a", "f", "a", "g"}); - test.AddOutput("Output", {6}, {false, false, false, true, true, true}); + test.AddInput("GrainInput", {6, 2}, {"a", "b", "a", "c", "a", "d", "a", "e", "a", "f", "a", "g"}); + test.AddInput("Non_GrainInput_1", {6, 2}, {"c", "c", "d", "d", "e", "e", "e", "e", "e", "e", "e", "e"}); + test.AddInput("Non_GrainInput_2", {6, 1}, {1, 2, 3, 4, 5, 6}); + + test.AddOutput("GrainOutput", {3, 2}, {"a", "b", "a", "c", "a", "d"}); + test.AddOutput("Non_GrainOutput_1", {3, 2}, {"c", "c", "d", "d", "e", "e"}); + test.AddOutput("Non_GrainOutput_2", {3, 1}, {1, 2, 3}); + + test.Run(); +} + +TEST(FeaturizersTests, ShortGrainDropperTransformer_Min_2) { + + EstimatorT estimator(NS::CreateTestAnnotationMapsPtr(1), 0, 2); + + std::vector>> trainingBatches = NS::TestHelpers::make_vector>>( + NS::TestHelpers::make_vector>( + NS::TestHelpers::make_vector("a", "b"), //false + NS::TestHelpers::make_vector("a", "b"), + NS::TestHelpers::make_vector("a", "b"), + NS::TestHelpers::make_vector("a", "b"), + NS::TestHelpers::make_vector("a", "b"), + NS::TestHelpers::make_vector("a", "c"), //false + NS::TestHelpers::make_vector("a", "c"), + NS::TestHelpers::make_vector("a", "c"), + NS::TestHelpers::make_vector("a", "c"), + NS::TestHelpers::make_vector("a", "d"), //false + NS::TestHelpers::make_vector("a", "d"), + NS::TestHelpers::make_vector("a", "d"), + NS::TestHelpers::make_vector("a", "e"), //false + NS::TestHelpers::make_vector("a", "e"), + NS::TestHelpers::make_vector("a", "f") //true + ) + ); + + auto stream = GetStream(estimator, trainingBatches); + auto dim = static_cast(stream.size()); + OpTester test("ShortGrainDropperTransformer", 1, onnxruntime::kMSFeaturizersDomain); + test.AddInput("State", {dim}, stream); + test.AddInput("GrainInput", {6, 2}, {"a", "b", "a", "c", "a", "d", "a", "e", "a", "f", "a", "g"}); + test.AddInput("Non_GrainInput_1", {6, 2}, {"c", "c", "d", "d", "e", "e", "e", "e", "e", "e", "e", "e"}); + test.AddInput("Non_GrainInput_2", {6, 1}, {1, 2, 3, 4, 5, 6}); + + test.AddOutput("GrainOutput", {4, 2}, {"a", "b", "a", "c", "a", "d", "a", "e"}); + test.AddOutput("Non_GrainOutput_1", {4, 2}, {"c", "c", "d", "d", "e", "e", "e", "e"}); + test.AddOutput("Non_GrainOutput_2", {4, 1}, {1, 2, 3, 4}); test.Run(); }