CPU GRU and LSTM Ops: Address corner case where output is uninitialized (#1193)

* Updated CPU GRU to zero Y output between max specified sequence length and max sequence length implied by input shape.

* Updated CPU LSTM to zero Y output between max specified sequence length and max sequence length implied by input shape.

* Disabled LSTMTest.ONNXRuntime_TestLSTMSequenceLengthShorterThanInputSequenceLength for nGraph execution provider and added TODO to investigate failure.
This commit is contained in:
edgchen1 2019-06-11 09:58:34 -07:00 committed by GitHub
parent 24d6b0f5c4
commit 0b9b429fe1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 94 additions and 4 deletions

View file

@ -808,6 +808,20 @@ void UniDirectionalGru<T>::Compute(const gsl::span<const T>& inputs_arg,
}
}
// zero any values beyond the evaluated steps
if (output_sequence && max_sequence_length < seq_length_) {
if (output_step_length == batch_size_ * hidden_size_) { // contiguous
const auto span_to_zero = outputs.subspan(
max_sequence_length * output_step_length, (seq_length_ - max_sequence_length) * output_step_length);
std::fill_n(span_to_zero.begin(), span_to_zero.size(), T{});
} else {
for (int i = max_sequence_length; i < seq_length_; ++i) { // non-contiguous
const auto span_to_zero = outputs.subspan(i * output_step_length, batch_size_ * hidden_size_);
std::fill_n(span_to_zero.begin(), span_to_zero.size(), T{});
}
}
}
if (output_sequence && direction_ == kReverse) {
ReverseSequence<T>(outputs, original_outputs,
sequence_lengths, seq_length_,

View file

@ -971,6 +971,20 @@ void UniDirectionalLstm<T>::Compute(const gsl::span<const T>& inputs_arg,
}
}
// zero any values beyond the evaluated steps
if (output_sequence && max_sequence_length < seq_length_) {
if (output_step_length == batch_size_ * hidden_size_) { // contiguous
const auto span_to_zero = outputs.subspan(
max_sequence_length * output_step_length, (seq_length_ - max_sequence_length) * output_step_length);
std::fill_n(span_to_zero.begin(), span_to_zero.size(), T{});
} else {
for (int i = max_sequence_length; i < seq_length_; ++i) { // non-contiguous
const auto span_to_zero = outputs.subspan(i * output_step_length, batch_size_ * hidden_size_);
std::fill_n(span_to_zero.begin(), span_to_zero.size(), T{});
}
}
}
if (output_sequence && direction_ == Direction::kReverse)
ReverseSequence<T>(outputs, original_outputs, sequence_lengths, seq_length_,
batch_size_, hidden_size_, num_directions);

View file

@ -241,7 +241,7 @@ void DefaultActivationsSimpleWeightsWithBias(std::string direction,
RunGruTest(X_data, W_data, R_data, Y_data, {}, input_size, batch_size, hidden_size, seq_length,
&B_data, nullptr, nullptr, direction, 999.f, /* output_sequence*/ true, linear_before_reset);
} // namespace test
}
TEST(GRUTest, ForwardDefaultActivationsSimpleWeightsWithBiasBatchParallel) {
std::vector<float> Y_data{
@ -336,7 +336,7 @@ class DeepCpuGruOpTestContext {
std::vector<float> gru_input_weights_;
std::vector<float> gru_recurrent_weights_;
std::vector<float> gru_bias_;
}; // namespace test
};
DeepCpuGruOpTestContext::DeepCpuGruOpTestContext(const std::string direction,
const std::vector<std::string>& activations,
@ -459,7 +459,7 @@ void DeepCpuGruOpTestContext::RunTest(const std::vector<float>& X,
const std::vector<float>& expected_Y,
const std::vector<float>& expected_Y_h,
const bool linear_before_reset) {
//run with and without output_sequence
//run with and without output_sequence
::onnxruntime::test::RunGruTest(X, gru_input_weights_, gru_recurrent_weights_,
expected_Y, expected_Y_h,
input_size_, batch_size, hidden_dim_, seq_length,
@ -469,7 +469,7 @@ void DeepCpuGruOpTestContext::RunTest(const std::vector<float>& X,
direction_,
9999999999.f,
/*output_sequence*/ true,
linear_before_reset,
linear_before_reset,
activation_func_names_,
alphas_,
betas_);
@ -819,6 +819,35 @@ TEST(GRUTest, ONNXRuntime_TestGRUOpSequenceLengthWithPartialZero) {
ctx.RunTest(X, batch_size, seq_length, sequence_length, &initial_h, expected_Y, expected_Y_h);
}
TEST(GRUTest, ONNXRuntime_TestGRUOpSequenceLengthShorterThanInputSequenceLength) {
const std::string direction = "bidirectional";
const std::vector<std::string> activations = {"sigmoid", "tanh", "sigmoid", "tanh"};
DeepCpuGruOpTestContext ctx(direction, activations);
const int batch = 1;
const int seq_length = 2;
std::vector<float> X = {-0.455351f, -0.276391f,
-0.185934f, -0.269585f};
std::vector<int> sequence_lengths = {1};
std::vector<float> initial_h = {0.0f, 0.0f,
-0.04566499f, 0.04621252f};
std::vector<float> expected_Y = {-0.03255286f, 0.0774838f,
-0.05469977f, 0.1004222f,
0.0f, 0.0f,
0.0f, 0.0f};
std::vector<float> expected_Y_h = {-0.03255286f, 0.0774838f,
-0.05469977f, 0.1004222f};
ctx.RunTest(X, batch, seq_length, sequence_lengths, &initial_h, expected_Y, expected_Y_h);
}
TEST(GRUTest, ONNXRuntime_TestGRUOpSequenceLengthAllZeros) {
const std::string direction = "forward";
const std::vector<std::string> activations = {"sigmoid", "tanh"};

View file

@ -1059,5 +1059,38 @@ TEST(LSTMTest, ONNXRuntime_TestLSTMSequenceLengthPartialZeros) {
&sequence_length, use_bias, use_peepholes);
}
// TODO this test fails for nGraph - need to investigate why
#ifndef USE_NGRAPH
TEST(LSTMTest, ONNXRuntime_TestLSTMSequenceLengthShorterThanInputSequenceLength) {
const int seq_len = 2;
const int batch_size = 1;
std::vector<float> X_data = {-0.455351f, -0.276391f,
-0.185934f, -0.269585f};
std::vector<int> sequence_length = {1};
std::vector<float> initial_h = {0.0f, 0.0f,
-0.0306872f, 0.028035f};
std::vector<float> initial_c = {0.0f, 0.0f,
-0.07243599f, 0.0467052f};
std::vector<float> Y_data = {-0.0251062f, 0.0561262f,
-0.0318928f, 0.0762679f,
0.0f, 0.0f,
0.0f, 0.0f};
std::vector<float> Y_h_data = {-0.0251062f, 0.0561262f,
-0.0318928f, 0.0762679f};
std::string direction = "bidirectional";
LstmOpContext2x1x2x2 context(direction);
context.RunTest(X_data, batch_size, seq_len, &initial_h, &initial_c, Y_data, Y_h_data, {}, &sequence_length);
}
#endif // USE_NGRAPH
} // namespace test
} // namespace onnxruntime