Add sequence identity for opset 14 & fix sequence insert (#7335)

**Description**: 
- Fix SequenceInsert with last position, which is equal to the current sequence length.
- Implement Identity to support sequence input for opset 14.

**Motivation and Context**
- Required to export Huggingface/transformers T5 with beam search.
This commit is contained in:
Bowen Bao 2021-04-28 13:26:57 -07:00 committed by GitHub
parent 22d7cde725
commit c584d48283
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 104 additions and 40 deletions

View file

@ -713,11 +713,11 @@ class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 14, Id
The process is the same for TYPED and untyped kernels - just repeat for each type when updating the typed entries.
The changes below in the registrations using BuildKernelCreateInfo are essentially the same. Update existing
The changes below in the registrations using BuildKernelCreateInfo are essentially the same. Update existing
registration to use the VERSIONED_ macro, add end version, add new un-versioned entry in the section for the new
opset.
To double-check what versions an operator should have registrations for see
To double-check what versions an operator should have registrations for see
https://github.com/onnx/onnx/blob/master/docs/Operators.md
*****/

View file

@ -209,10 +209,10 @@ Status SequenceInsert::Compute(OpKernelContext* context) const {
const auto* I = context->Input<Tensor>(2);
int64_t num_tensors_input_seq = static_cast<int64_t>(S->Size());
int64_t input_seq_idx = num_tensors_input_seq + 1; // default is append
if (I) { // position is optional
int64_t input_seq_idx = num_tensors_input_seq; // default is append
if (I) { // position is optional
input_seq_idx = GetSeqIdx(*I);
if (!ValidateSeqIdx(input_seq_idx, static_cast<int64_t>(num_tensors_input_seq))) {
if (!ValidateSeqIdx(input_seq_idx, num_tensors_input_seq) && input_seq_idx != num_tensors_input_seq) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"Invalid sequence index (", input_seq_idx, ") specified for sequence of size (", num_tensors_input_seq, ")");
}
@ -234,7 +234,7 @@ Status SequenceInsert::Compute(OpKernelContext* context) const {
CreateCopyAndAppendCpuTensor(S->Get(i), context, tensors);
}
}
if (input_seq_idx == num_tensors_input_seq + 1) {
if (input_seq_idx == num_tensors_input_seq) {
CreateCopyAndAppendCpuTensor(*X, context, tensors);
}

View file

@ -41,7 +41,7 @@ ONNX_CPU_OPERATOR_VERSIONED_KERNEL(
ONNX_CPU_OPERATOR_KERNEL(
Identity,
14,
KernelDefBuilder().TypeConstraint("V", DataTypeImpl::AllTensorTypes()).Alias(0, 0),
KernelDefBuilder().TypeConstraint("V", DataTypeImpl::AllTensorAndSequenceTensorTypes()).Alias(0, 0),
IdentityOp<false>);
} // namespace onnxruntime

View file

@ -12,6 +12,7 @@
#pragma warning(pop)
#endif
#include "core/framework/op_kernel.h"
#include "core/framework/TensorSeq.h"
namespace onnxruntime {
@ -22,39 +23,62 @@ class IdentityOp final : public OpKernel {
}
Status Compute(OpKernelContext* context) const override {
const auto* X = context->Input<Tensor>(0);
ORT_ENFORCE(X != nullptr);
const TensorShape& shape = X->Shape();
Tensor* Y = context->Output(0, shape);
auto X_type = X->DataType();
auto X_ml_type = context->InputType(0);
if (X_ml_type == DataTypeImpl::GetType<Tensor>()) {
const auto* X = context->Input<Tensor>(0);
ORT_ENFORCE(X != nullptr);
const TensorShape& shape = X->Shape();
Tensor* Y = context->Output(0, shape);
auto X_type = X->DataType();
const void* source = X->DataRaw(X_type);
void* target = Y->MutableDataRaw(X_type);
//If source and target pointers are not equal, we need to copy the data.
if (target != source) {
if (!X->IsDataTypeString()) {
memcpy(target, source, shape.Size() * X_type->Size());
} else {
// handle std::string
const auto* src = X->template Data<std::string>();
auto* dst = Y->template MutableData<std::string>();
std::copy(src, src + shape.Size(), dst);
const void* source = X->DataRaw(X_type);
void* target = Y->MutableDataRaw(X_type);
//If source and target pointers are not equal, we need to copy the data.
if (target != source) {
if (!X->IsDataTypeString()) {
memcpy(target, source, shape.Size() * X_type->Size());
} else {
// handle std::string
const auto* src = X->template Data<std::string>();
auto* dst = Y->template MutableData<std::string>();
std::copy(src, src + shape.Size(), dst);
}
}
}
if (is_dropout) {
Tensor* mask = context->Output(1, shape);
// a 'nullptr' returned would make it an unused optional output
if (mask != nullptr) {
// Opset 7 differs with Opset 10 in that the type of the 'mask'
// output is tied with the type of the input in Opset 7 whereas
// the type of 'mask' in Opset 10 is 'bool' always
// so we have a common solution
void* mask_data = mask->MutableDataRaw();
// In 'test'/'inference' mode, there are no input values dropped out
// so fill the buffer with 0/false
memset(mask_data, 0, mask->SizeInBytes());
if (is_dropout) {
Tensor* mask = context->Output(1, shape);
// a 'nullptr' returned would make it an unused optional output
if (mask != nullptr) {
// Opset 7 differs with Opset 10 in that the type of the 'mask'
// output is tied with the type of the input in Opset 7 whereas
// the type of 'mask' in Opset 10 is 'bool' always
// so we have a common solution
void* mask_data = mask->MutableDataRaw();
// In 'test'/'inference' mode, there are no input values dropped out
// so fill the buffer with 0/false
memset(mask_data, 0, mask->SizeInBytes());
}
}
} else {
const auto* X = context->Input<TensorSeq>(0);
ORT_ENFORCE(X != nullptr);
TensorSeq* output = context->Output<TensorSeq>(0);
output->SetType(X->DataType());
AllocatorPtr alloc;
auto status = context->GetTempSpaceAllocator(&alloc);
if (!status.IsOK()) {
ORT_THROW("Unable to get an allocator");
}
std::vector<Tensor> tensors;
for (auto it = X->begin(), end = X->end(); it != end; ++it) {
Tensor tmp(it->DataType(), onnxruntime::TensorShape(it->Shape()), alloc);
size_t bytes = it->SizeInBytes();
memcpy(tmp.MutableDataRaw(), it->DataRaw(), bytes);
tensors.push_back(std::move(tmp));
}
output->SetElements(std::move(tensors));
}
return Status::OK();

View file

@ -129,6 +129,36 @@ TEST(SequenceOpsTest, SequenceInsertPositiveDefault) {
test.Run();
}
TEST(SequenceOpsTest, SequenceInsertEmptyLast) {
OpTester test("SequenceInsert", 11);
SeqTensors<int64_t> input;
test.AddSeqInput("S", input);
test.AddInput<int64_t>("T", {3, 2}, {10, 20, 30, 40, 50, 60});
test.AddInput<int64_t>("I", {1}, {0});
SeqTensors<int64_t> output;
output.AddTensor({3, 2}, {10, 20, 30, 40, 50, 60});
test.AddSeqOutput("S2", output);
test.Run();
}
TEST(SequenceOpsTest, SequenceInsertLast) {
OpTester test("SequenceInsert", 11);
SeqTensors<int64_t> input;
input.AddTensor({3, 2}, {1, 2, 3, 4, 5, 6});
input.AddTensor({3, 3}, {1, 2, 3, 4, 5, 6, 7, 8, 9});
test.AddSeqInput("S", input);
test.AddInput<int64_t>("T", {3, 2}, {10, 20, 30, 40, 50, 60});
test.AddInput<int64_t>("I", {1}, {2});
SeqTensors<int64_t> output;
output.AddTensor({3, 2}, {1, 2, 3, 4, 5, 6});
output.AddTensor({3, 3}, {1, 2, 3, 4, 5, 6, 7, 8, 9});
output.AddTensor({3, 2}, {10, 20, 30, 40, 50, 60});
test.AddSeqOutput("S2", output);
test.Run();
}
TEST(SequenceOpsTest, SequenceInsertValidPositiveIdx) {
OpTester test("SequenceInsert", 11);
SeqTensors<int64_t> input;

View file

@ -23,5 +23,15 @@ TEST(Identity, StringType) {
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kTensorrtExecutionProvider});//TensorRT: unsupported data type
}
TEST(Identity, SequenceType) {
OpTester test("Identity", 14, kOnnxDomain);
SeqTensors<int64_t> input;
input.AddTensor({3, 2}, {1, 2, 3, 4, 5, 6});
input.AddTensor({3, 3}, {1, 2, 3, 4, 5, 6, 7, 8, 9});
test.AddSeqInput("X", input);
test.AddSeqOutput("Y", input);
test.Run();
}
} // namespace test
} // namespace onnxruntime

View file

@ -891,10 +891,6 @@
"HardSigmoid ai.onnx CPUExecutionProvider",
8073874088399301792
],
[
"Identity ai.onnx CPUExecutionProvider",
13401700334046441640
],
[
"Identity ai.onnx CPUExecutionProvider",
16879814636194901248
@ -903,6 +899,10 @@
"Identity ai.onnx CPUExecutionProvider",
18001636502361632792
],
[
"Identity ai.onnx CPUExecutionProvider",
16515685968327103576
],
[
"If ai.onnx CPUExecutionProvider",
2236645891232685176