Add weight prepacking to LSTM kernel (#5305)

This commit is contained in:
Tracy Sharpe 2020-09-29 13:33:38 -07:00 committed by GitHub
parent 11c194ce29
commit f07059ccc0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 213 additions and 91 deletions

View file

@ -272,7 +272,7 @@ Status DeepCpuGruOp::ComputeImpl(OpKernelContext& context) const {
int batch_size = gsl::narrow<int>(X_shape[1]);
int input_size = gsl::narrow<int>(X_shape[2]);
auto status = ValidateCommonRnnInputs(X, W, R, B, 3, sequence_lens, initial_h, num_directions_, hidden_size_);
auto status = ValidateCommonRnnInputs(X, W.Shape(), R.Shape(), B, 3, sequence_lens, initial_h, num_directions_, hidden_size_);
ORT_RETURN_IF_ERROR(status);
// GRU outputs are optional but must be in the same order

View file

@ -211,8 +211,8 @@ class UniDirectionalLstm {
const ActivationFuncs::Entry& activation_func_h, float clip, concurrency::ThreadPool* thread_pool);
void Compute(const gsl::span<const T>& inputs, const gsl::span<const int>& sequence_lengths, int num_directions,
const gsl::span<const T>& input_weights, const gsl::span<const T>& recurrent_weights,
gsl::span<T>& outputs, gsl::span<T>& final_hidden_state, gsl::span<T>& final_cell_state);
const GemmWeights<T>& input_weights, const GemmWeights<T>& recurrent_weights, gsl::span<T>& outputs,
gsl::span<T>& final_hidden_state, gsl::span<T>& final_cell_state);
~UniDirectionalLstm() = default;
@ -290,20 +290,74 @@ class UniDirectionalLstm {
} // namespace detail
Status DeepCpuLstmOp::TryPackWeights(const Tensor& weights, PackedWeights& packed_weights, bool& is_packed) {
const auto& shape = weights.Shape();
if (shape.NumDimensions() != 3) {
return Status::OK();
}
// weights: [num_directions, 4*hidden_size, input_size]
// recurrence weights: [num_directions, 4*hidden_size, hidden_size]
const size_t N = static_cast<size_t>(shape[1]);
const size_t K = static_cast<size_t>(shape[2]);
if ((shape[0] != num_directions_) || (N != static_cast<size_t>(hidden_size_ * 4))) {
return Status::OK();
}
const size_t packed_weights_size = MlasGemmPackBSize(N, K);
if (packed_weights_size == 0) {
return Status::OK();
}
auto alloc = Info().GetAllocator(0, OrtMemTypeDefault);
auto* packed_weights_data = alloc->Alloc(SafeInt<size_t>(packed_weights_size) * num_directions_);
packed_weights.buffer_ = BufferUniquePtr(packed_weights_data, BufferDeleter(alloc));
packed_weights.weights_size_ = packed_weights_size;
packed_weights.shape_ = shape;
const auto* weights_data = weights.Data<float>();
for (int i = 0; i < num_directions_; i++) {
MlasGemmPackB(CblasTrans, N, K, weights_data, K, packed_weights_data);
packed_weights_data = static_cast<uint8_t*>(packed_weights_data) + packed_weights_size;
weights_data += N * K;
}
is_packed = true;
return Status::OK();
}
#if !defined(USE_MKLML_FOR_BLAS)
Status DeepCpuLstmOp::PrePack(const Tensor& tensor, int input_idx, bool& is_packed) {
is_packed = false;
if (tensor.IsDataType<float>()) {
if (input_idx == 1) {
return TryPackWeights(tensor, packed_W_, is_packed);
} else if (input_idx == 2) {
return TryPackWeights(tensor, packed_R_, is_packed);
}
}
return Status::OK();
}
#endif
Status DeepCpuLstmOp::Compute(OpKernelContext* context) const {
const Tensor& X = *context->Input<Tensor>(0); // inputs. [seq_length, batch_size, input_size]
Status status;
// auto& logger = context->Logger();
if (X.IsDataType<float>())
if (X.IsDataType<float>()) {
status = ComputeImpl<float>(*context);
else if (X.IsDataType<double>()) {
} else if (X.IsDataType<double>()) {
/* Need to update all the helpers to support double...
status = ComputeImpl<double>(*context); */
ORT_NOT_IMPLEMENTED("LSTM operator does not support double yet");
} else
} else {
ORT_THROW("Invalid data type for LSTM operator of ", X.DataType());
}
return status;
}
@ -322,8 +376,10 @@ Status DeepCpuLstmOp::ComputeImpl(OpKernelContext& context) const {
auto& logger = context.Logger();
const Tensor& X = *context.Input<Tensor>(0); // inputs. [seq_length, batch_size, input_size]
const Tensor& W = *context.Input<Tensor>(1); // weights. [num_directions, 4*hidden_size, input_size]
const Tensor& R = *context.Input<Tensor>(2); // recurrence weights. [num_directions, 4*hidden_size, hidden_size]
const Tensor* W = packed_W_.buffer_ ? nullptr : context.Input<Tensor>(1);
// weights. [num_directions, 4*hidden_size, input_size]
const Tensor* R = packed_R_.buffer_ ? nullptr : context.Input<Tensor>(2);
// recurrence weights. [num_directions, 4*hidden_size, hidden_size]
// optional
const Tensor* B = context.Input<Tensor>(3); // bias. [num_directions, 8*hidden_size]
@ -332,13 +388,16 @@ Status DeepCpuLstmOp::ComputeImpl(OpKernelContext& context) const {
const Tensor* initial_c = context.Input<Tensor>(6); // initial cell. [num_directions, batch_size, hidden_size]
const Tensor* P = context.Input<Tensor>(7); // peephole weights. [num_directions, 3*hidden_size]
auto& X_shape = X.Shape();
const auto& X_shape = X.Shape();
int seq_length = gsl::narrow<int>(X_shape[0]);
int batch_size = gsl::narrow<int>(X_shape[1]);
int input_size = gsl::narrow<int>(X_shape[2]);
Status status = ValidateInputs(X, W, R, B, sequence_lens, initial_h, initial_c, P, batch_size);
const auto& W_shape = (W != nullptr) ? W->Shape() : packed_W_.shape_;
const auto& R_shape = (R != nullptr) ? R->Shape() : packed_R_.shape_;
Status status = ValidateInputs(X, W_shape, R_shape, B, sequence_lens, initial_h, initial_c, P, batch_size);
ORT_RETURN_IF_ERROR(status);
// LSTM outputs are optional but must be in the same order
@ -370,8 +429,9 @@ Status DeepCpuLstmOp::ComputeImpl(OpKernelContext& context) const {
status = context.GetTempSpaceAllocator(&alloc);
ORT_RETURN_IF_ERROR(status);
gsl::span<const T> input_weights = W.DataAsSpan<T>();
gsl::span<const T> recurrent_weights = R.DataAsSpan<T>();
const auto* input_weights = (W != nullptr) ? W->Data<T>() : nullptr;
const auto* recurrent_weights = (R != nullptr) ? R->Data<T>() : nullptr;
gsl::span<const T> bias = B != nullptr ? B->DataAsSpan<T>() : gsl::span<const T>();
gsl::span<const T> peephole_weights = P != nullptr ? P->DataAsSpan<T>() : gsl::span<const T>();
@ -381,8 +441,9 @@ Status DeepCpuLstmOp::ComputeImpl(OpKernelContext& context) const {
const size_t bias_size_per_direction = 8 * hidden_size_;
const size_t peephole_weights_size_per_direction = 3 * hidden_size_;
gsl::span<const T> input_weights_1 = input_weights.subspan(0, input_weights_size_per_direction);
gsl::span<const T> recurrent_weights_1 = recurrent_weights.subspan(0, hidden_weights_size_per_direction);
GemmWeights<T> input_weights_1(0, input_weights, input_weights_size_per_direction, packed_W_);
GemmWeights<T> recurrent_weights_1(0, recurrent_weights, hidden_weights_size_per_direction, packed_R_);
gsl::span<const T> bias_1 = bias.empty() ? bias : bias.subspan(0, bias_size_per_direction);
gsl::span<const T> peephole_weights_1 =
peephole_weights.empty() ? peephole_weights : peephole_weights.subspan(0, peephole_weights_size_per_direction);
@ -427,11 +488,10 @@ Status DeepCpuLstmOp::ComputeImpl(OpKernelContext& context) const {
gsl::span<T> last_cell_1 = last_cell.subspan(0, last_cell_size_per_direction);
if (direction_ == Direction::kBidirectional) {
GemmWeights<T> input_weights_2(1, input_weights, input_weights_size_per_direction, packed_W_);
GemmWeights<T> recurrent_weights_2(1, recurrent_weights, hidden_weights_size_per_direction, packed_R_);
// spans for second direction
gsl::span<const T> input_weights_2 =
input_weights.subspan(input_weights_size_per_direction, input_weights_size_per_direction);
gsl::span<const T> hidden_weights_2 =
recurrent_weights.subspan(hidden_weights_size_per_direction, hidden_weights_size_per_direction);
gsl::span<const T> bias_2 = bias.empty() ? bias : bias.subspan(bias_size_per_direction, bias_size_per_direction);
gsl::span<const T> peephole_weights_2 =
peephole_weights.empty() ? peephole_weights : peephole_weights.subspan(peephole_weights_size_per_direction, peephole_weights_size_per_direction);
@ -459,8 +519,8 @@ Status DeepCpuLstmOp::ComputeImpl(OpKernelContext& context) const {
fw.Compute(input, sequence_lens_span, num_directions_, input_weights_1, recurrent_weights_1, output_1,
hidden_output_1, last_cell_1);
bw.Compute(input, sequence_lens_span, num_directions_, input_weights_2, hidden_weights_2, output_2, hidden_output_2,
last_cell_2);
bw.Compute(input, sequence_lens_span, num_directions_, input_weights_2, recurrent_weights_2, output_2,
hidden_output_2, last_cell_2);
} else {
detail::UniDirectionalLstm<T> fw(alloc, logger, seq_length, batch_size, input_size, hidden_size_, direction_,
input_forget_, bias_1, peephole_weights_1, initial_hidden_1, initial_cell_1,
@ -481,11 +541,11 @@ Status DeepCpuLstmOp::ComputeImpl(OpKernelContext& context) const {
return Status::OK();
}
Status DeepCpuLstmOp::ValidateInputs(const Tensor& X, const Tensor& W, const Tensor& R, const Tensor* B,
const Tensor* sequence_lens, const Tensor* initial_h, const Tensor* initial_c,
const Tensor* P, int batch_size) const {
Status DeepCpuLstmOp::ValidateInputs(const Tensor& X, const TensorShape& W_shape, const TensorShape& R_shape,
const Tensor* B, const Tensor* sequence_lens, const Tensor* initial_h,
const Tensor* initial_c, const Tensor* P, int batch_size) const {
auto status =
rnn::detail::ValidateCommonRnnInputs(X, W, R, B, 4, sequence_lens, initial_h, num_directions_, hidden_size_);
rnn::detail::ValidateCommonRnnInputs(X, W_shape, R_shape, B, 4, sequence_lens, initial_h, num_directions_, hidden_size_);
ORT_RETURN_IF_ERROR(status);
if (initial_c != nullptr) {
@ -680,8 +740,8 @@ void UniDirectionalLstm<T>::LoadBias(const gsl::span<const T>& WbRb_values) {
template <typename T>
void UniDirectionalLstm<T>::Compute(const gsl::span<const T>& inputs_arg,
const gsl::span<const int>& sequence_lengths_arg, const int num_directions,
const gsl::span<const T>& input_weights,
const gsl::span<const T>& recurrent_weights, gsl::span<T>& outputs,
const GemmWeights<T>& input_weights, const GemmWeights<T>& recurrent_weights,
gsl::span<T>& outputs,
gsl::span<T>& final_hidden_state, gsl::span<T>& final_cell_state) {
// copy spans (just T* and size, not data in span) as we may change them
gsl::span<const T> inputs = inputs_arg;
@ -736,9 +796,9 @@ void UniDirectionalLstm<T>::Compute(const gsl::span<const T>& inputs_arg,
const int total_rows = max_sequence_length * batch_size_;
// apply the weights to all the inputs and save to output_IOFC
ComputeGemm(total_rows, hidden_size_x4, input_size_, alpha, inputs.cbegin(), inputs.cend(), input_size_,
input_weights.cbegin(), input_weights.cend(), // W[iofc]
input_size_, beta, output_iofc_.begin(), output_iofc_.end(), hidden_size_x4, thread_pool_);
ComputeGemm(total_rows, hidden_size_x4, input_size_, alpha, inputs.cbegin(), inputs.cend(),
input_weights,
beta, output_iofc_.begin(), output_iofc_.end(), hidden_size_x4, thread_pool_);
DumpMatrix("Xt*(W[iofc]^T)", output_iofc_.data(), total_rows, hidden_size_x4);
@ -783,10 +843,10 @@ void UniDirectionalLstm<T>::Compute(const gsl::span<const T>& inputs_arg,
// calculate Xt*(W[iofc]^T) + Ht-t*R[iofc]
// Do it sequentially to avoid nested parallelism
ComputeGemm(local_fused_hidden_rows, hidden_size_x4, hidden_size_, alpha, previous_state,
previous_state_end, // Ht-1
hidden_size_, recurrent_weights.cbegin(), recurrent_weights.cend(), // R[iofc]
hidden_size_, beta, step_out_IOFC, output_iofc_.end(), // input contains Xt*(W[iofc]^T)
ComputeGemm(local_fused_hidden_rows, hidden_size_x4, hidden_size_, alpha,
previous_state, previous_state_end, // Ht-1
recurrent_weights, // R[iofc]
beta, step_out_IOFC, output_iofc_.end(), // input contains Xt*(W[iofc]^T)
hidden_size_x4, nullptr);
DumpMatrix("Xt*(W[iofc]^T) + Ht-t*R[iofc]" + row_str, &*step_out_IOFC, local_fused_hidden_rows, hidden_size_x4);
@ -861,9 +921,10 @@ void UniDirectionalLstm<T>::Compute(const gsl::span<const T>& inputs_arg,
span_T_iter step_out_IOFC = output_iofc_.begin() + (step * batch_size_) * hidden_size_x4;
// calculate Xt*(W[iofc]^T) + Ht-t*R[iofc]
ComputeGemm(batch_size_, hidden_size_x4, hidden_size_, alpha, previous_state, previous_state_end, // Ht-1
hidden_size_, recurrent_weights.cbegin(), recurrent_weights.cend(), // R[iofc]
hidden_size_, beta, step_out_IOFC, output_iofc_.end(), // input contains Xt*(W[iofc]^T)
ComputeGemm(batch_size_, hidden_size_x4, hidden_size_, alpha,
previous_state, previous_state_end, // Ht-1
recurrent_weights, // R[iofc]
beta, step_out_IOFC, output_iofc_.end(), // input contains Xt*(W[iofc]^T)
hidden_size_x4, thread_pool_);
span_T_iter batched_output;

View file

@ -50,17 +50,22 @@ class DeepCpuLstmOp final : public OpKernel {
activation_func_betas);
}
#if !defined(USE_MKLML_FOR_BLAS)
Status PrePack(const Tensor& tensor, int input_idx, bool& is_packed) override;
#endif
Status Compute(OpKernelContext* context) const override;
~DeepCpuLstmOp() override = default;
private:
Status TryPackWeights(const Tensor& weights, rnn::detail::PackedWeights& packed_weights, bool& is_packed);
template <typename T>
Status ComputeImpl(OpKernelContext& context) const;
Status ValidateInputs(const Tensor& X,
const Tensor& W,
const Tensor& R,
const TensorShape& W,
const TensorShape& R,
const Tensor* B,
const Tensor* sequence_lens,
const Tensor* initial_h,
@ -75,6 +80,9 @@ class DeepCpuLstmOp final : public OpKernel {
float clip_;
bool input_forget_ = false;
rnn::detail::PackedWeights packed_W_;
rnn::detail::PackedWeights packed_R_;
rnn::detail::ActivationFuncs activation_funcs_;
};

View file

@ -119,7 +119,7 @@ Status RNN<float>::Compute(OpKernelContext* ctx) const {
int64_t batch_size = X.Shape()[1];
int64_t input_size = X.Shape()[2];
auto status = rnn::detail::ValidateCommonRnnInputs(X, W, R, B, 1, sequence_lens, initial_h,
auto status = rnn::detail::ValidateCommonRnnInputs(X, W.Shape(), R.Shape(), B, 1, sequence_lens, initial_h,
num_directions, hidden_size_);
ORT_RETURN_IF_ERROR(status);

View file

@ -24,8 +24,8 @@ namespace detail {
using namespace ::onnxruntime::common;
Status ValidateCommonRnnInputs(const Tensor& X,
const Tensor& W,
const Tensor& R,
const TensorShape& W_shape,
const TensorShape& R_shape,
const Tensor* B,
int WRB_dim_1_multipler,
const Tensor* sequence_lens,
@ -33,8 +33,6 @@ Status ValidateCommonRnnInputs(const Tensor& X,
int64_t num_directions,
int64_t hidden_size) {
auto& X_shape = X.Shape();
auto& W_shape = W.Shape();
auto& R_shape = R.Shape();
int64_t seq_length = X_shape[0];
int64_t batch_size = X_shape[1];

View file

@ -12,7 +12,8 @@
#include "core/framework/allocator.h"
#include "core/util/math.h"
#include "core/util/math_cpuonly.h"
#include "core/mlas/inc/mlas.h"
#include "core/common/safeint.h"
#include "core/platform/threadpool.h"
#include "gsl/gsl"
@ -70,8 +71,8 @@ gsl::span<TAlloc> Allocate(std::shared_ptr<IAllocator> allocator,
// validate the common inputs to RNN, LSTM and GRU operators
Status ValidateCommonRnnInputs(const Tensor& X,
const Tensor& W,
const Tensor& R,
const TensorShape& W_shape,
const TensorShape& R_shape,
const Tensor* B,
int WRB_dim_1_multipler, // multiplier used with hidden_size for W, R and B inputs
const Tensor* sequence_lens,
@ -145,7 +146,8 @@ void ComputeGemm(const int M,
const float beta,
TSpanCIter C,
TSpanCIter C_end,
const int ldc, concurrency::ThreadPool* tp) {
const int ldc,
concurrency::ThreadPool* thread_pool) {
// validate all the inputs
// need to use the lda/ldb/ldc strides which should be >= the columns for the span
ORT_ENFORCE(lda >= K && ldb >= K && ldc >= N);
@ -158,7 +160,64 @@ void ComputeGemm(const int M,
M, N, K, alpha,
&*A, lda,
&*B, ldb, beta,
&*C, ldc, tp);
&*C, ldc, thread_pool);
}
struct PackedWeights {
BufferUniquePtr buffer_;
size_t weights_size_;
TensorShape shape_;
};
template <typename T>
struct GemmWeights {
GemmWeights(int idx, const T* weights_data, size_t weights_size, const PackedWeights& packed_weights) {
if (packed_weights.buffer_) {
is_prepacked_ = true;
buffer_ = static_cast<uint8_t*>(packed_weights.buffer_.get()) + packed_weights.weights_size_ * idx;
} else {
is_prepacked_ = false;
buffer_ = weights_data + weights_size * idx;
}
}
bool is_prepacked_;
const void* buffer_;
};
template <typename TSpanAIter, typename TSpanCIter>
void ComputeGemm(const int M,
const int N,
const int K,
const float alpha,
TSpanAIter A,
TSpanAIter A_end,
const GemmWeights<float>& weights,
const float beta,
TSpanCIter C,
TSpanCIter C_end,
const int ldc,
concurrency::ThreadPool* thread_pool) {
// validate all the inputs
// need to use the lda/ldb/ldc strides which should be >= the columns for the span
ORT_ENFORCE(A + (M * K) <= A_end);
ORT_ENFORCE(C + (M * ldc - (ldc - N)) <= C_end);
if (weights.is_prepacked_) {
MlasGemm(
CblasNoTrans,
M, N, K, alpha,
&*A, K,
weights.buffer_, beta,
&*C, ldc, thread_pool);
} else {
::onnxruntime::math::GemmEx<float>(
CblasNoTrans, CblasTrans,
M, N, K, alpha,
&*A, K,
static_cast<const float *>(weights.buffer_), K, beta,
&*C, ldc, thread_pool);
}
}
// helper to convert a span to a raw pointer

View file

@ -25,7 +25,9 @@ T DuplicateContainer(const T& container) {
static void RunLstmTest(const std::vector<float>& X_data,
const std::vector<float>& W_data,
bool is_initializer_W,
const std::vector<float>& R_data,
bool is_initializer_R,
const std::vector<float>& Y_data,
const std::vector<float>& Y_h_data,
const std::vector<float>& Y_c_data,
@ -78,8 +80,8 @@ static void RunLstmTest(const std::vector<float>& X_data,
std::vector<int64_t> R_dims = {num_directions, 4 * hidden_size, hidden_size};
test.AddInput<float>("X", X_dims, X_data);
test.AddInput<float>("W", W_dims, W_data);
test.AddInput<float>("R", R_dims, R_data);
test.AddInput<float>("W", W_dims, W_data, is_initializer_W);
test.AddInput<float>("R", R_dims, R_data, is_initializer_R);
if (B_data) {
std::vector<int64_t> B_dims = {num_directions, 8 * hidden_size};
@ -169,14 +171,14 @@ void SimpleWeightsNoBiasTwoRows(std::string direction,
W_data = DuplicateContainer(W_data);
}
RunLstmTest(X_data, W_data, R_data, Y_data, Y_h_data, Y_c_data,
RunLstmTest(X_data, W_data, false, R_data, false, Y_data, Y_h_data, Y_c_data,
input_size, batch_size, hidden_size, seq_length,
nullptr, nullptr, nullptr, nullptr, seq_lengths, direction);
// need at least one output, so we need Y_h or Y_c to be requested (non-empty output to compare against) in order
// to test Y not being returned (output_sequence == false)
if (!Y_h_data.empty() || !Y_c_data.empty())
RunLstmTest(X_data, W_data, R_data, Y_data, Y_h_data, Y_c_data,
RunLstmTest(X_data, W_data, false, R_data, false, Y_data, Y_h_data, Y_c_data,
input_size, batch_size, hidden_size, seq_length,
nullptr, nullptr, nullptr, nullptr, seq_lengths, direction, 999.f, /* output_sequence*/ false);
}
@ -367,8 +369,12 @@ TEST(LSTMTest, BatchParallelFalseSeqLengthGreaterThanOne) {
std::vector<float> Y_c_data{
1.02721067f, 1.15254318f};
RunLstmTest(X_data, W_data, R_data, Y_data, {}, Y_c_data,
input_size, batch_size, hidden_size, seq_length);
for (bool is_initializer_W : std::initializer_list<bool>{false, true}) {
for (bool is_initializer_R : std::initializer_list<bool>{false, true}) {
RunLstmTest(X_data, W_data, is_initializer_W, R_data, is_initializer_R,
Y_data, {}, Y_c_data, input_size, batch_size, hidden_size, seq_length);
}
}
}
// make sure GateComputations works correctly if batch_parallel_ is true due to large batch size
@ -393,9 +399,13 @@ static void LargeBatchWithClip(const std::vector<float>& Y_h_data, float clip =
std::vector<float> R_data(num_directions * 4 * hidden_size * hidden_size, 0.1f);
RunLstmTest(X_data, W_data, R_data, {}, Y_h_data, {},
input_size, batch_size, hidden_size, seq_length,
nullptr, nullptr, nullptr, nullptr, nullptr, direction, clip);
for (bool is_initializer_W : std::initializer_list<bool>{false, true}) {
for (bool is_initializer_R : std::initializer_list<bool>{false, true}) {
RunLstmTest(X_data, W_data, is_initializer_W, R_data, is_initializer_R, {},
Y_h_data, {}, input_size, batch_size, hidden_size, seq_length,
nullptr, nullptr, nullptr, nullptr, nullptr, direction, clip);
}
}
}
TEST(LSTMTest, LargeBatchNoClipping) {
@ -613,37 +623,25 @@ class LstmOpContext2x1x2x2 {
bool input_forget = false,
bool hasClip = true) {
// run with and without output_sequence to test UniDirectionalLstm handling when Y isn't returned
::onnxruntime::test::RunLstmTest(X, input_weights_, recurrent_weights_,
expected_Y, expected_Y_h, expected_Y_c,
input_size_, batch_size, hidden_size_, seq_length,
use_bias ? &bias_ : nullptr,
use_peepholes ? &peephole_weights_ : nullptr,
initial_h, initial_c,
sequence_lens,
direction_,
clip,
/*output_sequence*/ true,
input_forget,
activation_func_names_,
activation_alphas_,
activation_betas_,
hasClip);
::onnxruntime::test::RunLstmTest(X, input_weights_, recurrent_weights_,
expected_Y, expected_Y_h, expected_Y_c,
input_size_, batch_size, hidden_size_, seq_length,
use_bias ? &bias_ : nullptr,
use_peepholes ? &peephole_weights_ : nullptr,
initial_h, initial_c,
sequence_lens,
direction_,
clip,
/*output_sequence*/ false,
input_forget,
activation_func_names_,
activation_alphas_,
activation_betas_,
hasClip);
for (bool output_sequence : std::initializer_list<bool>{false, true}) {
::onnxruntime::test::RunLstmTest(X,
input_weights_, false,
recurrent_weights_, false,
expected_Y, expected_Y_h, expected_Y_c,
input_size_, batch_size, hidden_size_, seq_length,
use_bias ? &bias_ : nullptr,
use_peepholes ? &peephole_weights_ : nullptr,
initial_h, initial_c,
sequence_lens,
direction_,
clip,
output_sequence,
input_forget,
activation_func_names_,
activation_alphas_,
activation_betas_,
hasClip);
}
}
private:
@ -669,10 +667,8 @@ TEST(LSTMTest, ONNXRuntime_TestLSTMForwardPeepHole) {
std::vector<float> Y_h_data = {-0.03277518f, 0.05935364f};
std::vector<float> Y_c_data = {-0.0780206f, 0.098829f};
std::string direction = "forward";
//Run Test
LstmOpContext2x1x2x2 context(direction);
LstmOpContext2x1x2x2 context("forward");
context.RunTest(input, batch_size, seq_len, nullptr, nullptr, Y_data, Y_h_data, Y_c_data);
}