1375 lines
54 KiB
C++
1375 lines
54 KiB
C++
// Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "paddle/phi/kernels/rnn_grad_kernel.h"
|
|
|
|
#include "paddle/phi/backends/cpu/cpu_context.h"
|
|
#include "paddle/phi/core/kernel_registry.h"
|
|
#include "paddle/phi/core/tensor_utils.h"
|
|
#include "paddle/phi/kernels/cpu/rnn_functor.h"
|
|
#include "paddle/phi/kernels/funcs/activation_functor.h"
|
|
#include "paddle/phi/kernels/funcs/blas/blas.h"
|
|
#include "paddle/phi/kernels/funcs/concat_and_split_functor.h"
|
|
#include "paddle/phi/kernels/funcs/detail/activation_functions.h"
|
|
#include "paddle/phi/kernels/funcs/gru_compute.h"
|
|
#include "paddle/phi/kernels/funcs/lstm_compute.h"
|
|
#include "paddle/phi/kernels/funcs/math_function.h"
|
|
|
|
namespace phi {
|
|
|
|
template <typename T>
|
|
void BackupTensor(const CPUContext& dev_ctx,
|
|
DenseTensor* dst,
|
|
DenseTensor* src) {
|
|
dst->Resize(src->dims()); // NOLINT
|
|
dev_ctx.Alloc<T>(dst);
|
|
Copy(dev_ctx, *src, dev_ctx.GetPlace(), false, dst);
|
|
}
|
|
|
|
template <typename T>
|
|
void CreateLstmValue(funcs::LstmMetaValue<T>* lstm_value) {
|
|
lstm_value->check_ig = nullptr;
|
|
lstm_value->check_fg = nullptr;
|
|
lstm_value->check_og = nullptr;
|
|
}
|
|
|
|
template <typename T>
|
|
void CreateLstmGrad(funcs::LstmMetaGrad<T>* lstm_grad) {
|
|
lstm_grad->check_ig_grad = nullptr;
|
|
lstm_grad->check_fg_grad = nullptr;
|
|
lstm_grad->check_og_grad = nullptr;
|
|
}
|
|
|
|
template <typename T>
|
|
struct GradCell {
|
|
virtual ~GradCell() = default;
|
|
virtual void operator()(const CPUContext& dev_ctx UNUSED,
|
|
DenseTensor* gate_tensor UNUSED,
|
|
DenseTensor* state_tensor UNUSED,
|
|
DenseTensor* act_state_tensor UNUSED,
|
|
DenseTensor* hidden_tensor UNUSED,
|
|
const DenseTensor* weight_hh UNUSED,
|
|
DenseTensor* pre_hidden UNUSED,
|
|
DenseTensor* pre_state UNUSED,
|
|
DenseTensor* grad_hidden UNUSED,
|
|
DenseTensor* grad_state UNUSED,
|
|
DenseTensor* grad_gate UNUSED,
|
|
DenseTensor* grad_weight_hh UNUSED,
|
|
DenseTensor* grad_pre_hidden UNUSED,
|
|
DenseTensor* grad_pre_state UNUSED,
|
|
DenseTensor* grad_bias_hh UNUSED,
|
|
const DenseTensor& mask_tensor UNUSED,
|
|
bool has_sequence_length UNUSED) const {}
|
|
|
|
void postprocess_pre_hidden_grad(const CPUContext& dev_ctx,
|
|
DenseTensor* grad_pre_hidden,
|
|
DenseTensor* grad_pre_hidden_bak,
|
|
DenseTensor* grad_pre_state,
|
|
DenseTensor* grad_pre_state_bak,
|
|
const DenseTensor& mask_tensor,
|
|
bool has_sequence_length) const {
|
|
if (has_sequence_length) {
|
|
auto& place = *dev_ctx.eigen_device();
|
|
auto mask = EigenMatrix<T>::From(mask_tensor,
|
|
make_ddim({mask_tensor.dims()[1], 1}));
|
|
auto mask_broadcast = mask.broadcast(Eigen::DSizes<int, 2>(
|
|
1, static_cast<int>(grad_pre_hidden->dims()[2])));
|
|
auto pre_hidden_grad = EigenMatrix<T>::Reshape(
|
|
*grad_pre_hidden, grad_pre_hidden->dims().size() - 1);
|
|
auto pre_hidden_bak_grad = EigenMatrix<T>::Reshape(
|
|
*grad_pre_hidden_bak, grad_pre_hidden_bak->dims().size() - 1);
|
|
pre_hidden_grad.device(place) =
|
|
(1 - mask_broadcast) * pre_hidden_bak_grad +
|
|
pre_hidden_grad * mask_broadcast;
|
|
if (grad_pre_state) {
|
|
auto pre_state_grad = EigenMatrix<T>::Reshape(
|
|
*grad_pre_state, grad_pre_state->dims().size() - 1);
|
|
auto pre_state_bak_grad = EigenMatrix<T>::Reshape(
|
|
*grad_pre_state_bak, grad_pre_state_bak->dims().size() - 1);
|
|
pre_state_grad.device(place) =
|
|
(1 - mask_broadcast) * pre_state_bak_grad +
|
|
pre_state_grad * mask_broadcast;
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual void update_pre_hidden_grad(const CPUContext& dev_ctx,
|
|
DenseTensor* grad_gate,
|
|
const DenseTensor* weight_hh,
|
|
DenseTensor* grad_pre_hidden,
|
|
DenseTensor* grad_pre_hidden_bak,
|
|
DenseTensor* grad_pre_state,
|
|
DenseTensor* grad_pre_state_bak,
|
|
const DenseTensor& mask_tensor,
|
|
bool has_sequence_length) const {
|
|
auto blas = funcs::GetBlas<CPUContext, T>(dev_ctx);
|
|
DenseTensor* grad_gate_tmp = grad_gate;
|
|
auto mat_dim_a =
|
|
funcs::CreateMatrixDescriptor(grad_gate_tmp->dims(), 0, false);
|
|
mat_dim_a.height_ *= mat_dim_a.batch_size_;
|
|
mat_dim_a.batch_size_ = 0;
|
|
auto mat_dim_b = funcs::CreateMatrixDescriptor(weight_hh->dims(), 0, false);
|
|
blas.MatMul(*grad_gate_tmp,
|
|
mat_dim_a,
|
|
*weight_hh,
|
|
mat_dim_b,
|
|
static_cast<T>(1.0),
|
|
grad_pre_hidden,
|
|
0);
|
|
postprocess_pre_hidden_grad(dev_ctx,
|
|
grad_pre_hidden,
|
|
grad_pre_hidden_bak,
|
|
grad_pre_state,
|
|
grad_pre_state_bak,
|
|
mask_tensor,
|
|
has_sequence_length);
|
|
}
|
|
|
|
virtual void update_weight_hh_grad(const CPUContext& dev_ctx,
|
|
DenseTensor* grad_gate,
|
|
DenseTensor* pre_hidden,
|
|
DenseTensor* grad_weight_hh) const {
|
|
auto blas = funcs::GetBlas<CPUContext, T>(dev_ctx);
|
|
auto mat_dim_c = funcs::CreateMatrixDescriptor(grad_gate->dims(), 0, true);
|
|
mat_dim_c.height_ *= mat_dim_c.batch_size_;
|
|
mat_dim_c.batch_size_ = 0;
|
|
auto mat_dim_d =
|
|
funcs::CreateMatrixDescriptor(pre_hidden->dims(), 0, false);
|
|
mat_dim_d.height_ *= mat_dim_d.batch_size_;
|
|
mat_dim_d.batch_size_ = 0;
|
|
blas.MatMul(*grad_gate,
|
|
mat_dim_c,
|
|
*pre_hidden,
|
|
mat_dim_d,
|
|
static_cast<T>(1.0),
|
|
grad_weight_hh,
|
|
static_cast<T>(1.0));
|
|
}
|
|
};
|
|
|
|
template <typename T, template <typename> class EigenActivationBackwardFunctor>
|
|
struct SimpleRNNGradCell : GradCell<T> {
|
|
void operator()(const CPUContext& dev_ctx,
|
|
DenseTensor* gate_tensor,
|
|
DenseTensor* state_tensor UNUSED,
|
|
DenseTensor* act_state_tensor UNUSED,
|
|
DenseTensor* hidden_tensor,
|
|
const DenseTensor* weight_hh,
|
|
DenseTensor* pre_hidden,
|
|
DenseTensor* pre_state UNUSED,
|
|
DenseTensor* grad_hidden,
|
|
DenseTensor* grad_state UNUSED,
|
|
DenseTensor* grad_gate,
|
|
DenseTensor* grad_weight_hh,
|
|
DenseTensor* grad_pre_hidden,
|
|
DenseTensor* grad_pre_state UNUSED,
|
|
DenseTensor* grad_bias_hh UNUSED,
|
|
const DenseTensor& mask_tensor,
|
|
bool has_sequence_length) const override {
|
|
DenseTensor grad_pre_hidden_bak;
|
|
if (has_sequence_length) {
|
|
BackupTensor<T>(dev_ctx, &grad_pre_hidden_bak, grad_pre_hidden);
|
|
}
|
|
// h = act(z)
|
|
// update dz
|
|
auto dz = EigenVector<T>::Flatten(
|
|
GET_DATA_SAFELY(grad_gate, "Output", "dz", "Grad"));
|
|
auto dh = EigenVector<T>::Flatten(
|
|
GET_DATA_SAFELY(grad_hidden, "Input", "dh", "Grad"));
|
|
auto h = EigenVector<T>::Flatten(
|
|
GET_DATA_SAFELY(hidden_tensor, "Input", "h", "Value"));
|
|
// useless, but need this argument to execute functor
|
|
auto z = EigenVector<T>::Flatten(
|
|
GET_DATA_SAFELY(gate_tensor, "Input", "z", "Value"));
|
|
|
|
auto* place = dev_ctx.eigen_device();
|
|
EigenActivationBackwardFunctor<T> functor;
|
|
functor(*place, z, h, dh, dz);
|
|
|
|
// update grad_weight_hh, grad_pre_hidden
|
|
this->update_pre_hidden_grad(dev_ctx,
|
|
grad_gate,
|
|
weight_hh,
|
|
grad_pre_hidden,
|
|
&grad_pre_hidden_bak,
|
|
nullptr,
|
|
nullptr,
|
|
mask_tensor,
|
|
has_sequence_length);
|
|
this->update_weight_hh_grad(dev_ctx, grad_gate, pre_hidden, grad_weight_hh);
|
|
}
|
|
};
|
|
|
|
template <typename T>
|
|
struct GRUGradCell : GradCell<T> {
|
|
void operator()(const CPUContext& dev_ctx,
|
|
DenseTensor* gate_tensor,
|
|
DenseTensor* state_tensor,
|
|
DenseTensor* act_state_tensor UNUSED,
|
|
DenseTensor* hidden_tensor UNUSED,
|
|
const DenseTensor* weight_hh,
|
|
DenseTensor* pre_hidden,
|
|
DenseTensor* pre_state UNUSED,
|
|
DenseTensor* grad_hidden,
|
|
DenseTensor* grad_state,
|
|
DenseTensor* grad_gate,
|
|
DenseTensor* grad_weight_hh,
|
|
DenseTensor* grad_pre_hidden,
|
|
DenseTensor* grad_pre_state UNUSED,
|
|
DenseTensor* grad_bias_hh,
|
|
const DenseTensor& mask_tensor,
|
|
bool has_sequence_length) const override {
|
|
size_t frame_size = pre_hidden->dims()[2];
|
|
size_t batch_size = pre_hidden->dims()[1];
|
|
DenseTensor grad_pre_hidden_bak;
|
|
if (has_sequence_length) {
|
|
BackupTensor<T>(dev_ctx, &grad_pre_hidden_bak, grad_pre_hidden);
|
|
}
|
|
// zero pre_hidden
|
|
funcs::SetConstant<CPUContext, T> zero;
|
|
zero(dev_ctx, grad_pre_hidden, static_cast<T>(0.0));
|
|
funcs::GRUMetaValue<T> gru_value;
|
|
funcs::GRUMetaGrad<T> gru_grad;
|
|
gru_value.gate_value = gate_tensor->data<T>();
|
|
gru_value.prev_out_value = pre_hidden->data<T>();
|
|
gru_value.reset_output_value = state_tensor->data<T>();
|
|
gru_value.state_weight = weight_hh->data<T>() + 2 * frame_size * frame_size;
|
|
gru_value.gate_weight = weight_hh->data<T>();
|
|
|
|
gru_grad.gate_grad = grad_gate->data<T>();
|
|
gru_grad.reset_output_grad = grad_state->data<T>(); // NOLINT
|
|
gru_grad.prev_out_grad = grad_pre_hidden->data<T>();
|
|
gru_grad.output_grad = grad_hidden->data<T>();
|
|
gru_grad.gate_weight_grad = grad_weight_hh->data<T>();
|
|
gru_grad.state_weight_grad =
|
|
grad_weight_hh->data<T>() + 2 * frame_size * frame_size;
|
|
gru_grad.bias_hh_grad = grad_bias_hh->data<T>();
|
|
|
|
auto act_gate = funcs::detail::GetActivationType("sigmoid_v2");
|
|
auto act_node = funcs::detail::GetActivationType("tanh_v2");
|
|
funcs::GRUUnitGradFunctorV2<CPUContext, T>::compute(dev_ctx,
|
|
gru_value,
|
|
gru_grad,
|
|
frame_size,
|
|
batch_size,
|
|
act_node,
|
|
act_gate);
|
|
|
|
this->postprocess_pre_hidden_grad(dev_ctx,
|
|
grad_pre_hidden,
|
|
&grad_pre_hidden_bak,
|
|
nullptr,
|
|
nullptr,
|
|
mask_tensor,
|
|
has_sequence_length);
|
|
}
|
|
};
|
|
|
|
template <typename T>
|
|
struct LSTMGradCell : GradCell<T> {
|
|
void operator()(const CPUContext& dev_ctx,
|
|
DenseTensor* gate_tensor,
|
|
DenseTensor* state_tensor,
|
|
DenseTensor* act_state_tensor,
|
|
DenseTensor* hidden_tensor UNUSED,
|
|
const DenseTensor* weight_hh,
|
|
DenseTensor* pre_hidden,
|
|
DenseTensor* pre_state,
|
|
DenseTensor* grad_hidden,
|
|
DenseTensor* grad_state,
|
|
DenseTensor* grad_gate,
|
|
DenseTensor* grad_weight_hh,
|
|
DenseTensor* grad_pre_hidden,
|
|
DenseTensor* grad_pre_state,
|
|
DenseTensor* grad_bias_hh UNUSED,
|
|
const DenseTensor& mask_tensor,
|
|
bool has_sequence_length) const override {
|
|
size_t frame_size = state_tensor->dims()[2];
|
|
size_t batch_size = state_tensor->dims()[1];
|
|
|
|
DenseTensor grad_pre_hidden_bak;
|
|
DenseTensor grad_pre_state_bak;
|
|
if (has_sequence_length) {
|
|
BackupTensor<T>(dev_ctx, &grad_pre_hidden_bak, grad_pre_hidden);
|
|
BackupTensor<T>(dev_ctx, &grad_pre_state_bak, grad_pre_state);
|
|
}
|
|
|
|
funcs::LstmMetaValue<T> lstm_value;
|
|
funcs::LstmMetaGrad<T> lstm_grad;
|
|
CreateLstmValue(&lstm_value);
|
|
CreateLstmGrad(&lstm_grad);
|
|
lstm_value.gate_value = gate_tensor->data<T>();
|
|
lstm_value.state_value = state_tensor->data<T>();
|
|
lstm_value.state_active_value = act_state_tensor->data<T>();
|
|
lstm_value.prev_state_value = pre_state->data<T>(); // NOLINT
|
|
|
|
lstm_grad.state_grad = grad_state->data<T>(); // NOLINT
|
|
lstm_grad.gate_grad = grad_gate->data<T>();
|
|
lstm_grad.output_grad = grad_hidden->data<T>();
|
|
lstm_grad.prev_state_grad = grad_pre_state->data<T>();
|
|
|
|
lstm_value.output_value = nullptr;
|
|
lstm_grad.state_active_grad = nullptr;
|
|
|
|
auto gate_act = funcs::detail::GetActivationType("sigmoid_v2");
|
|
auto state_act = funcs::detail::GetActivationType("tanh_v2");
|
|
auto cand_act = funcs::detail::GetActivationType("tanh_v2");
|
|
|
|
T cell_clip = 0.0;
|
|
funcs::LstmUnitGradFunctor<CPUContext, T>::compute(dev_ctx,
|
|
lstm_value,
|
|
lstm_grad,
|
|
frame_size,
|
|
batch_size,
|
|
cell_clip,
|
|
gate_act,
|
|
state_act,
|
|
cand_act,
|
|
false);
|
|
this->update_pre_hidden_grad(dev_ctx,
|
|
grad_gate,
|
|
weight_hh,
|
|
grad_pre_hidden,
|
|
&grad_pre_hidden_bak,
|
|
grad_pre_state,
|
|
&grad_pre_state_bak,
|
|
mask_tensor,
|
|
has_sequence_length);
|
|
this->update_weight_hh_grad(dev_ctx, grad_gate, pre_hidden, grad_weight_hh);
|
|
}
|
|
};
|
|
|
|
template <typename T, typename GradCellType>
|
|
struct GradLayer {
|
|
explicit GradLayer(const GradCellType& cell) : cell_(cell) {}
|
|
virtual ~GradLayer() = default;
|
|
void run_rnn_grad_function(
|
|
const CPUContext& dev_ctx,
|
|
const DenseTensor* input,
|
|
DenseTensor* input_grad,
|
|
const DenseTensor* sequence_length,
|
|
std::vector<DenseTensor>* init_h_unbind,
|
|
std::vector<DenseTensor>* init_c_unbind,
|
|
std::vector<DenseTensor>* init_h_grad_unbind,
|
|
std::vector<DenseTensor>* init_c_grad_unbind,
|
|
DenseTensor* layer_grad_gate_tensor,
|
|
std::vector<DenseTensor>* layer_gate_tensor_unbind,
|
|
std::vector<DenseTensor>* layer_grad_gate_tensor_unbind,
|
|
std::vector<DenseTensor>* layer_state_tensor_unbind,
|
|
std::vector<DenseTensor>* layer_act_state_tensor_unbind,
|
|
std::vector<DenseTensor>* output_tensor_unbind,
|
|
std::vector<DenseTensor>* output_grad_tensor_unbind,
|
|
const std::vector<DenseTensor>& last_h_grad_unbind,
|
|
const std::vector<DenseTensor>& last_c_grad_unbind,
|
|
const std::vector<std::vector<DenseTensor>>& parameter_lists,
|
|
std::vector<std::vector<DenseTensor>>* weight_list_grad,
|
|
int layer_idx,
|
|
int time_step,
|
|
bool has_sequence_length,
|
|
bool is_bidirec,
|
|
bool is_reverse,
|
|
const std::string& mode) {
|
|
int direction_num = is_bidirec ? 2 : 1;
|
|
int current_reverse_idx = is_reverse ? 1 : 0;
|
|
int current_layer_idx = direction_num * layer_idx + current_reverse_idx;
|
|
int begin_idx = 0;
|
|
if (is_reverse) {
|
|
begin_idx = time_step;
|
|
}
|
|
|
|
DenseTensor mask_matrix;
|
|
std::vector<DenseTensor> mask_tensor_list;
|
|
int mask_min_length = time_step;
|
|
if (has_sequence_length) {
|
|
mask_matrix.Resize({time_step, input->dims()[1]});
|
|
CreateMaskMatrix<T>(
|
|
dev_ctx, sequence_length, &mask_matrix, is_reverse, &mask_min_length);
|
|
mask_tensor_list = Unbind(mask_matrix);
|
|
}
|
|
// copy the last_h, last_c for swapping pointer
|
|
DenseTensor a, b;
|
|
DenseTensor* dynamic_grad_last_h = &a;
|
|
DenseTensor* dynamic_grad_last_c = &b;
|
|
dynamic_grad_last_h->Resize(last_h_grad_unbind[current_layer_idx].dims());
|
|
dev_ctx.Alloc<T>(dynamic_grad_last_h);
|
|
Copy(dev_ctx,
|
|
last_h_grad_unbind[current_layer_idx],
|
|
dev_ctx.GetPlace(),
|
|
false,
|
|
dynamic_grad_last_h);
|
|
if (!last_c_grad_unbind.empty()) {
|
|
dynamic_grad_last_c->Resize(last_c_grad_unbind[current_layer_idx].dims());
|
|
dev_ctx.Alloc<T>(dynamic_grad_last_c);
|
|
Copy(dev_ctx,
|
|
last_c_grad_unbind[current_layer_idx],
|
|
dev_ctx.GetPlace(),
|
|
false,
|
|
dynamic_grad_last_c);
|
|
} else {
|
|
dynamic_grad_last_c = nullptr;
|
|
}
|
|
|
|
DenseTensor c, d;
|
|
DenseTensor* dynamic_grad_pre_h = &c;
|
|
DenseTensor* dynamic_grad_pre_c = &d;
|
|
funcs::SetConstant<CPUContext, T> zero;
|
|
if (!init_h_grad_unbind->empty()) {
|
|
dynamic_grad_pre_h->ShareDataWith(
|
|
(*init_h_grad_unbind)[current_layer_idx]);
|
|
} else {
|
|
dynamic_grad_pre_h->Resize(dynamic_grad_last_h->dims());
|
|
dev_ctx.Alloc<T>(dynamic_grad_pre_h);
|
|
zero(dev_ctx, dynamic_grad_pre_h, static_cast<T>(0.0));
|
|
}
|
|
if (!init_c_grad_unbind->empty()) {
|
|
dynamic_grad_pre_c->ShareDataWith(
|
|
(*init_c_grad_unbind)[current_layer_idx]);
|
|
} else {
|
|
if (is_lstm(mode) || is_gru(mode)) {
|
|
dynamic_grad_pre_c->Resize(dynamic_grad_last_h->dims());
|
|
dev_ctx.Alloc<T>(dynamic_grad_pre_c);
|
|
if (is_gru(mode)) {
|
|
dynamic_grad_last_c = dynamic_grad_pre_c;
|
|
}
|
|
} else {
|
|
dynamic_grad_pre_c = nullptr;
|
|
}
|
|
}
|
|
|
|
if (is_reverse) {
|
|
// must be reverse the input, output, input_grad, output_grad
|
|
// the gate and grad_gate must be reverse
|
|
std::reverse(layer_gate_tensor_unbind->begin(),
|
|
layer_gate_tensor_unbind->end());
|
|
std::reverse(layer_grad_gate_tensor_unbind->begin(),
|
|
layer_grad_gate_tensor_unbind->end());
|
|
/*
|
|
if (has_sequence_length) {
|
|
std::reverse(mask_tensor_list.begin(), mask_tensor_list.end());
|
|
}*/
|
|
std::reverse(output_tensor_unbind->begin(), output_tensor_unbind->end());
|
|
std::reverse(output_grad_tensor_unbind->begin(),
|
|
output_grad_tensor_unbind->end());
|
|
}
|
|
|
|
DenseTensor* weight_grad =
|
|
&((*weight_list_grad)[layer_idx][current_reverse_idx * 4 + 1]);
|
|
dev_ctx.Alloc<T>(weight_grad);
|
|
zero(dev_ctx, weight_grad, static_cast<T>(0.0));
|
|
|
|
DenseTensor* pre_hidden = nullptr;
|
|
DenseTensor* pre_state = nullptr;
|
|
DenseTensor* hidden = nullptr;
|
|
if (is_gru(mode)) {
|
|
zero(dev_ctx,
|
|
&((*weight_list_grad)[layer_idx][current_reverse_idx * 4 + 3]),
|
|
static_cast<T>(0.0));
|
|
}
|
|
for (int i = time_step - 1; i >= 0; --i) {
|
|
if (has_sequence_length) {
|
|
this->mask_preprocess(dev_ctx,
|
|
&(*output_grad_tensor_unbind)[i],
|
|
dynamic_grad_last_h,
|
|
dynamic_grad_last_c,
|
|
dynamic_grad_pre_h,
|
|
dynamic_grad_pre_c,
|
|
mask_tensor_list[i],
|
|
mode);
|
|
} else {
|
|
this->preprocess(
|
|
dev_ctx, &(*output_grad_tensor_unbind)[i], dynamic_grad_last_h);
|
|
}
|
|
hidden = &(*output_tensor_unbind)[i];
|
|
if (i == 0) {
|
|
pre_hidden = &(*init_h_unbind)[current_layer_idx];
|
|
if (!init_c_unbind->empty()) {
|
|
pre_state = &(*init_c_unbind)[current_layer_idx];
|
|
}
|
|
} else {
|
|
pre_hidden = &(*output_tensor_unbind)[i - 1];
|
|
if (!layer_state_tensor_unbind->empty()) {
|
|
pre_state = &(*layer_state_tensor_unbind)[begin_idx + i - 1];
|
|
}
|
|
}
|
|
this->cell_(
|
|
dev_ctx,
|
|
&(*layer_gate_tensor_unbind)[i],
|
|
&(*layer_state_tensor_unbind)[begin_idx + i],
|
|
&(*layer_act_state_tensor_unbind)[begin_idx + i],
|
|
hidden,
|
|
&(parameter_lists[layer_idx][current_reverse_idx * 4 + 1]),
|
|
pre_hidden,
|
|
pre_state,
|
|
dynamic_grad_last_h,
|
|
dynamic_grad_last_c,
|
|
&(*layer_grad_gate_tensor_unbind)[i],
|
|
weight_grad,
|
|
dynamic_grad_pre_h,
|
|
dynamic_grad_pre_c,
|
|
&((*weight_list_grad)[layer_idx][current_reverse_idx * 4 + 3]),
|
|
mask_tensor_list[i],
|
|
has_sequence_length);
|
|
SwapPointer(&dynamic_grad_last_h, &dynamic_grad_pre_h);
|
|
SwapPointer(&dynamic_grad_last_c, &dynamic_grad_pre_c);
|
|
}
|
|
// postproces for gradient for w_hi, X, bias_hi, bias_hh
|
|
this->postprocess(dev_ctx,
|
|
*layer_grad_gate_tensor,
|
|
*input,
|
|
input_grad,
|
|
parameter_lists[layer_idx],
|
|
&((*weight_list_grad)[layer_idx]),
|
|
is_reverse,
|
|
mode);
|
|
|
|
// copy the gradient to init_c init_h
|
|
if (!(*init_h_grad_unbind).empty() && time_step % 2 == 0) {
|
|
Copy(dev_ctx,
|
|
*dynamic_grad_last_h,
|
|
dev_ctx.GetPlace(),
|
|
false,
|
|
&((*init_h_grad_unbind)[current_layer_idx]));
|
|
}
|
|
if (!(*init_c_grad_unbind).empty() && time_step % 2 == 0) {
|
|
Copy(dev_ctx,
|
|
*dynamic_grad_last_c,
|
|
dev_ctx.GetPlace(),
|
|
false,
|
|
&((*init_c_grad_unbind)[current_layer_idx]));
|
|
}
|
|
}
|
|
|
|
virtual void operator()(
|
|
const CPUContext& dev_ctx UNUSED,
|
|
const DenseTensor* input UNUSED,
|
|
const DenseTensor* output UNUSED,
|
|
const std::vector<DenseTensor>& init_h_unbind UNUSED,
|
|
const std::vector<DenseTensor>& init_c_unbind UNUSED,
|
|
const std::vector<DenseTensor>& last_h_grad_unbind UNUSED,
|
|
const std::vector<DenseTensor>& last_c_grad_unbind UNUSED,
|
|
const std::vector<DenseTensor>& gate_tensor_unbind UNUSED,
|
|
const std::vector<DenseTensor>& state_tensor_unbind UNUSED,
|
|
const std::vector<DenseTensor>& act_state_tensor_unbind UNUSED,
|
|
const DenseTensor* output_grad UNUSED,
|
|
const std::vector<std::vector<DenseTensor>>& parameter_lists UNUSED,
|
|
const DenseTensor* sequence_length UNUSED,
|
|
DenseTensor* input_grad UNUSED,
|
|
std::vector<DenseTensor>* init_h_grad_unbind UNUSED,
|
|
std::vector<DenseTensor>* init_c_grad_unbind UNUSED,
|
|
const std::vector<std::vector<DenseTensor>>& weight_list_grad UNUSED,
|
|
int layer_idx UNUSED,
|
|
bool is_bidirec UNUSED,
|
|
int hidden_size UNUSED,
|
|
const std::string& mode UNUSED,
|
|
int gate_num UNUSED) {}
|
|
|
|
void preprocess(const CPUContext& dev_ctx,
|
|
const DenseTensor* grad_output,
|
|
DenseTensor* grad_last_h) {
|
|
auto& place = *dev_ctx.eigen_device();
|
|
auto output_grad =
|
|
EigenMatrix<T>::Reshape(*grad_output, grad_output->dims().size() - 1);
|
|
auto last_h_grad =
|
|
EigenMatrix<T>::Reshape(*grad_last_h, grad_last_h->dims().size() - 1);
|
|
// the output gradient contribute the gradient to last_h
|
|
last_h_grad.device(place) = last_h_grad + output_grad;
|
|
}
|
|
|
|
void mask_preprocess(const CPUContext& dev_ctx,
|
|
const DenseTensor* grad_output,
|
|
DenseTensor* grad_last_h,
|
|
DenseTensor* grad_last_c,
|
|
DenseTensor* grad_pre_h,
|
|
DenseTensor* grad_pre_c,
|
|
const DenseTensor& mask_tensor,
|
|
const std::string& mode) {
|
|
auto& place = *dev_ctx.eigen_device();
|
|
auto mask = EigenMatrix<T>::From(mask_tensor,
|
|
make_ddim({mask_tensor.dims()[1], 1}));
|
|
auto mask_broadcast = mask.broadcast(
|
|
Eigen::DSizes<int, 2>(1, static_cast<int>(grad_output->dims()[2])));
|
|
|
|
auto last_h_grad =
|
|
EigenMatrix<T>::Reshape(*grad_last_h, grad_last_h->dims().size() - 1);
|
|
auto pre_h_grad =
|
|
EigenMatrix<T>::Reshape(*grad_pre_h, grad_pre_h->dims().size() - 1);
|
|
auto output_grad =
|
|
EigenMatrix<T>::Reshape(*grad_output, grad_output->dims().size() - 1);
|
|
last_h_grad.device(place) = last_h_grad + output_grad * mask_broadcast;
|
|
pre_h_grad.device(place) = (1 - mask_broadcast) * last_h_grad;
|
|
last_h_grad.device(place) = mask_broadcast * last_h_grad;
|
|
|
|
if (grad_last_c && grad_pre_c && is_lstm(mode)) {
|
|
auto last_c_grad =
|
|
EigenMatrix<T>::Reshape(*grad_last_c, grad_last_c->dims().size() - 1);
|
|
auto pre_c_grad =
|
|
EigenMatrix<T>::Reshape(*grad_pre_c, grad_pre_c->dims().size() - 1);
|
|
pre_c_grad.device(place) = (1 - mask_broadcast) * last_c_grad;
|
|
last_c_grad.device(place) = mask_broadcast * last_c_grad;
|
|
}
|
|
}
|
|
|
|
void postprocess(const CPUContext& dev_ctx,
|
|
const DenseTensor& grad_gate,
|
|
const DenseTensor& input,
|
|
DenseTensor* input_grad,
|
|
const std::vector<DenseTensor>& parameters,
|
|
std::vector<DenseTensor>* grad_parameters,
|
|
int is_reverse,
|
|
const std::string& mode) {
|
|
// we get the grad_gate step by step, and need to bradocast the grad to the
|
|
// grad_w_hi, grad_bias_hi, grad_bias_hh
|
|
int begin_idx = 0;
|
|
if (is_reverse) {
|
|
begin_idx = 4;
|
|
}
|
|
auto blas = funcs::GetBlas<CPUContext, T>(dev_ctx);
|
|
|
|
// calc the gradient for the w_hi
|
|
auto mat_dim_out_grad =
|
|
funcs::CreateMatrixDescriptor(grad_gate.dims(), 0, true);
|
|
auto mat_dim_input = funcs::CreateMatrixDescriptor(input.dims(), 0, false);
|
|
mat_dim_out_grad.width_ *= mat_dim_out_grad.batch_size_;
|
|
mat_dim_out_grad.batch_size_ = 0;
|
|
mat_dim_input.height_ *= mat_dim_input.batch_size_;
|
|
mat_dim_input.batch_size_ = 0;
|
|
blas.MatMul(grad_gate,
|
|
mat_dim_out_grad,
|
|
input,
|
|
mat_dim_input,
|
|
static_cast<T>(1.0),
|
|
&((*grad_parameters)[begin_idx + 0]),
|
|
T(0));
|
|
|
|
// calc the gradient for the X
|
|
auto mat_dim_out_grad_new =
|
|
funcs::CreateMatrixDescriptor(grad_gate.dims(), 0, false);
|
|
mat_dim_out_grad_new.height_ *= mat_dim_out_grad_new.batch_size_;
|
|
mat_dim_out_grad_new.batch_size_ = 0;
|
|
auto mat_dim_parameter =
|
|
funcs::CreateMatrixDescriptor(parameters[0].dims(), 0, false);
|
|
blas.MatMul(grad_gate,
|
|
mat_dim_out_grad_new,
|
|
parameters[begin_idx + 0],
|
|
mat_dim_parameter,
|
|
static_cast<T>(1.0),
|
|
input_grad,
|
|
T(1));
|
|
|
|
// calc the gradient of Bias_hi, Bias_hh
|
|
funcs::ColwiseSum<CPUContext, T> col_sum;
|
|
DenseTensor tmp_grad_gate;
|
|
tmp_grad_gate.ShareDataWith(grad_gate);
|
|
tmp_grad_gate.Resize(
|
|
{grad_gate.dims()[0] * grad_gate.dims()[1], grad_gate.dims()[2]});
|
|
col_sum(dev_ctx, tmp_grad_gate, &((*grad_parameters)[begin_idx + 2]));
|
|
// Bias_hh
|
|
if (!is_gru(mode)) {
|
|
col_sum(dev_ctx, tmp_grad_gate, &((*grad_parameters)[begin_idx + 3]));
|
|
}
|
|
}
|
|
GradCellType cell_;
|
|
};
|
|
|
|
template <typename T, typename GradCellType>
|
|
struct SingleGradLayer : GradLayer<T, GradCellType> {
|
|
// explicit SingleGradLayer(GradCellType& cell) : cell_(cell) {}
|
|
explicit SingleGradLayer(const GradCellType& cell)
|
|
: GradLayer<T, GradCellType>(cell) {}
|
|
~SingleGradLayer() override = default;
|
|
using GradLayer<T, GradCellType>::operator();
|
|
void operator()(const CPUContext& dev_ctx,
|
|
const DenseTensor* input,
|
|
const DenseTensor* output,
|
|
std::vector<DenseTensor>* init_h_unbind,
|
|
std::vector<DenseTensor>* init_c_unbind,
|
|
const std::vector<DenseTensor>& last_h_grad_unbind,
|
|
const std::vector<DenseTensor>& last_c_grad_unbind,
|
|
const std::vector<DenseTensor>& gate_tensor_unbind,
|
|
const std::vector<DenseTensor>& state_tensor_unbind,
|
|
const std::vector<DenseTensor>& act_state_tensor_unbind,
|
|
const DenseTensor* output_grad,
|
|
const std::vector<std::vector<DenseTensor>>& parameter_lists,
|
|
const DenseTensor* sequence_length,
|
|
DenseTensor* input_grad,
|
|
std::vector<DenseTensor>* init_h_grad_unbind,
|
|
std::vector<DenseTensor>* init_c_grad_unbind,
|
|
std::vector<std::vector<DenseTensor>>* weight_list_grad,
|
|
int layer_idx,
|
|
bool is_bidirec,
|
|
int hidden_size,
|
|
const std::string& mode,
|
|
int gate_num) {
|
|
funcs::SetConstant<CPUContext, T> zero;
|
|
zero(dev_ctx, input_grad, static_cast<T>(0.0));
|
|
|
|
int time_step = static_cast<int>(input->dims()[0]);
|
|
int batch_size = static_cast<int>(input->dims()[1]);
|
|
int direction_num = is_bidirec ? 2 : 1;
|
|
|
|
// in this section, create the gate_state_grad for the postprocess calculate
|
|
// ubind the output, the output from [time_step, batch_size, hidden_size]
|
|
auto output_tensor_unbind = Unbind(*output);
|
|
auto output_grad_tensor_unbind = Unbind(*output_grad);
|
|
auto layer_gate_tensor = gate_tensor_unbind[layer_idx];
|
|
layer_gate_tensor.Resize(
|
|
{time_step * direction_num, batch_size, hidden_size * gate_num});
|
|
auto layer_gate_tensor_unbind = Unbind(layer_gate_tensor);
|
|
// the gate_tensor and the grad_gate_tensor must be unbind
|
|
DenseTensor layer_grad_gate_tensor;
|
|
layer_grad_gate_tensor.Resize(layer_gate_tensor.dims());
|
|
dev_ctx.Alloc<T>(&layer_grad_gate_tensor);
|
|
auto layer_grad_gate_tensor_unbind = Unbind(layer_grad_gate_tensor);
|
|
|
|
DenseTensor layer_state_tensor;
|
|
std::vector<DenseTensor> layer_state_tensor_unbind;
|
|
if (!state_tensor_unbind.empty()) {
|
|
layer_state_tensor = state_tensor_unbind[layer_idx];
|
|
layer_state_tensor.Resize(
|
|
{time_step * direction_num, batch_size, hidden_size});
|
|
layer_state_tensor_unbind = Unbind(layer_state_tensor);
|
|
}
|
|
|
|
DenseTensor layer_act_state_tensor;
|
|
std::vector<DenseTensor> layer_act_state_tensor_unbind;
|
|
if (!act_state_tensor_unbind.empty()) {
|
|
layer_act_state_tensor = act_state_tensor_unbind[layer_idx];
|
|
layer_act_state_tensor.Resize(
|
|
{time_step * direction_num, batch_size, hidden_size});
|
|
layer_act_state_tensor_unbind = Unbind(layer_act_state_tensor);
|
|
}
|
|
bool has_sequence_length = sequence_length == nullptr ? false : true;
|
|
this->run_rnn_grad_function(dev_ctx,
|
|
input,
|
|
input_grad,
|
|
sequence_length,
|
|
init_h_unbind,
|
|
init_c_unbind,
|
|
init_h_grad_unbind,
|
|
init_c_grad_unbind,
|
|
&layer_grad_gate_tensor,
|
|
&layer_gate_tensor_unbind,
|
|
&layer_grad_gate_tensor_unbind,
|
|
&layer_state_tensor_unbind,
|
|
&layer_act_state_tensor_unbind,
|
|
&output_tensor_unbind,
|
|
&output_grad_tensor_unbind,
|
|
last_h_grad_unbind,
|
|
last_c_grad_unbind,
|
|
parameter_lists,
|
|
weight_list_grad,
|
|
layer_idx,
|
|
time_step,
|
|
has_sequence_length,
|
|
is_bidirec,
|
|
false,
|
|
mode);
|
|
}
|
|
};
|
|
|
|
template <typename T>
|
|
void split_tensor_at_last_dim(const CPUContext& dev_ctx,
|
|
const DenseTensor* output,
|
|
std::vector<DenseTensor*>* output_vec,
|
|
int axis) {
|
|
std::vector<const DenseTensor*> shape_refer;
|
|
(*output_vec)[0]->Resize(
|
|
{output->dims()[0], output->dims()[1], output->dims()[2] / 2});
|
|
dev_ctx.Alloc<T>((*output_vec)[0]);
|
|
(*output_vec)[1]->Resize(
|
|
{output->dims()[0], output->dims()[1], output->dims()[2] / 2});
|
|
dev_ctx.Alloc<T>((*output_vec)[1]);
|
|
shape_refer.emplace_back((*output_vec)[0]);
|
|
shape_refer.emplace_back((*output_vec)[1]);
|
|
funcs::SplitFunctor<CPUContext, T> functor;
|
|
functor(dev_ctx, *output, shape_refer, axis, output_vec);
|
|
}
|
|
|
|
template <typename T, typename GradCellType>
|
|
struct BidirGradLayer : GradLayer<T, GradCellType> {
|
|
explicit BidirGradLayer(const GradCellType& cell)
|
|
: GradLayer<T, GradCellType>(cell) {}
|
|
~BidirGradLayer() override = default;
|
|
using GradLayer<T, GradCellType>::operator();
|
|
void operator()(const CPUContext& dev_ctx,
|
|
const DenseTensor* input,
|
|
const DenseTensor* output,
|
|
std::vector<DenseTensor>* init_h_unbind,
|
|
std::vector<DenseTensor>* init_c_unbind,
|
|
const std::vector<DenseTensor>& last_h_grad_unbind,
|
|
const std::vector<DenseTensor>& last_c_grad_unbind,
|
|
const std::vector<DenseTensor>& gate_tensor_unbind,
|
|
const std::vector<DenseTensor>& state_tensor_unbind,
|
|
const std::vector<DenseTensor>& act_state_tensor_unbind,
|
|
const DenseTensor* output_grad,
|
|
const std::vector<std::vector<DenseTensor>>& parameter_lists,
|
|
const DenseTensor* sequence_length,
|
|
DenseTensor* input_grad,
|
|
std::vector<DenseTensor>* init_h_grad_unbind,
|
|
std::vector<DenseTensor>* init_c_grad_unbind,
|
|
std::vector<std::vector<DenseTensor>>* weight_list_grad,
|
|
int layer_idx,
|
|
bool is_bidirec,
|
|
int hidden_size,
|
|
const std::string& mode,
|
|
int gate_num) {
|
|
int time_step = static_cast<int>(input->dims()[0]);
|
|
int batch_size = static_cast<int>(input->dims()[1]);
|
|
int direction_num = is_bidirec ? 2 : 1;
|
|
// split the output two tensor to output_forward, output_backward
|
|
funcs::SetConstant<CPUContext, T> zero;
|
|
zero(dev_ctx, input_grad, static_cast<T>(0.0));
|
|
|
|
std::vector<DenseTensor*> output_vec;
|
|
DenseTensor forward_output;
|
|
DenseTensor backward_output;
|
|
std::vector<DenseTensor> forward_output_tensor_unbind;
|
|
std::vector<DenseTensor> backward_output_tensor_unbind;
|
|
// in the last layer, we will use the output as the last hidden
|
|
// the output just the concat the forward hidden, backward hidden, so just
|
|
// split it
|
|
// in other layer, we just split the hidden in the rows
|
|
output_vec.emplace_back(&forward_output);
|
|
output_vec.emplace_back(&backward_output);
|
|
split_tensor_at_last_dim<T>(dev_ctx, output, &output_vec, 2);
|
|
forward_output_tensor_unbind = Unbind(*(output_vec[0]));
|
|
backward_output_tensor_unbind = Unbind(*(output_vec[1]));
|
|
|
|
std::vector<DenseTensor*> output_grad_vec;
|
|
DenseTensor grad_forward_output;
|
|
DenseTensor grad_backward_output;
|
|
output_grad_vec.emplace_back(&grad_forward_output);
|
|
output_grad_vec.emplace_back(&grad_backward_output);
|
|
split_tensor_at_last_dim<T>(dev_ctx, output_grad, &output_grad_vec, 2);
|
|
auto forward_output_grad_tensor_unbind = Unbind(*(output_grad_vec[0]));
|
|
auto backward_output_grad_tensor_unbind = Unbind(*(output_grad_vec[1]));
|
|
|
|
// the gate_tensor and the grad_gate_tensor must be unbind
|
|
auto layer_gate_tensor = gate_tensor_unbind[layer_idx];
|
|
layer_gate_tensor.Resize(
|
|
{time_step * 2, batch_size, hidden_size * gate_num});
|
|
auto layer_forward_gate_tensor = layer_gate_tensor.Slice(0, time_step);
|
|
auto layer_backward_gate_tensor =
|
|
layer_gate_tensor.Slice(time_step, 2 * time_step);
|
|
auto layer_forward_gate_tensor_unbind = Unbind(layer_forward_gate_tensor);
|
|
auto layer_backward_gate_tensor_unbind = Unbind(layer_backward_gate_tensor);
|
|
|
|
DenseTensor layer_grad_gate_tensor;
|
|
layer_grad_gate_tensor.Resize(layer_gate_tensor.dims());
|
|
dev_ctx.Alloc<T>(&layer_grad_gate_tensor);
|
|
zero(dev_ctx, &layer_grad_gate_tensor, static_cast<T>(0.0));
|
|
auto layer_forward_grad_gate_tensor =
|
|
layer_grad_gate_tensor.Slice(0, time_step);
|
|
auto layer_backward_grad_gate_tensor =
|
|
layer_grad_gate_tensor.Slice(time_step, 2 * time_step);
|
|
auto layer_forward_grad_gate_tensor_unbind =
|
|
Unbind(layer_forward_grad_gate_tensor);
|
|
auto layer_backward_grad_gate_tensor_unbind =
|
|
Unbind(layer_backward_grad_gate_tensor);
|
|
|
|
DenseTensor layer_state_tensor;
|
|
std::vector<DenseTensor> layer_state_tensor_unbind;
|
|
if (!state_tensor_unbind.empty()) {
|
|
layer_state_tensor = state_tensor_unbind[layer_idx];
|
|
layer_state_tensor.Resize(
|
|
{time_step * direction_num, batch_size, hidden_size});
|
|
layer_state_tensor_unbind = Unbind(layer_state_tensor);
|
|
}
|
|
|
|
DenseTensor layer_act_state_tensor;
|
|
std::vector<DenseTensor> layer_act_state_tensor_unbind;
|
|
if (!act_state_tensor_unbind.empty()) {
|
|
layer_act_state_tensor = act_state_tensor_unbind[layer_idx];
|
|
layer_act_state_tensor.Resize(
|
|
{time_step * direction_num, batch_size, hidden_size});
|
|
layer_act_state_tensor_unbind = Unbind(layer_act_state_tensor);
|
|
}
|
|
const bool& has_sequence_length = sequence_length == nullptr ? false : true;
|
|
|
|
this->run_rnn_grad_function(dev_ctx,
|
|
input,
|
|
input_grad,
|
|
sequence_length,
|
|
init_h_unbind,
|
|
init_c_unbind,
|
|
init_h_grad_unbind,
|
|
init_c_grad_unbind,
|
|
&layer_forward_grad_gate_tensor,
|
|
&layer_forward_gate_tensor_unbind,
|
|
&layer_forward_grad_gate_tensor_unbind,
|
|
&layer_state_tensor_unbind,
|
|
&layer_act_state_tensor_unbind,
|
|
&forward_output_tensor_unbind,
|
|
&forward_output_grad_tensor_unbind,
|
|
last_h_grad_unbind,
|
|
last_c_grad_unbind,
|
|
parameter_lists,
|
|
weight_list_grad,
|
|
layer_idx,
|
|
time_step,
|
|
has_sequence_length,
|
|
is_bidirec,
|
|
false,
|
|
mode);
|
|
|
|
this->run_rnn_grad_function(dev_ctx,
|
|
input,
|
|
input_grad,
|
|
sequence_length,
|
|
init_h_unbind,
|
|
init_c_unbind,
|
|
init_h_grad_unbind,
|
|
init_c_grad_unbind,
|
|
&layer_backward_grad_gate_tensor,
|
|
&layer_backward_gate_tensor_unbind,
|
|
&layer_backward_grad_gate_tensor_unbind,
|
|
&layer_state_tensor_unbind,
|
|
&layer_act_state_tensor_unbind,
|
|
&backward_output_tensor_unbind,
|
|
&backward_output_grad_tensor_unbind,
|
|
last_h_grad_unbind,
|
|
last_c_grad_unbind,
|
|
parameter_lists,
|
|
weight_list_grad,
|
|
layer_idx,
|
|
time_step,
|
|
has_sequence_length,
|
|
is_bidirec,
|
|
true,
|
|
mode);
|
|
}
|
|
};
|
|
|
|
template <typename T>
|
|
void dropout_cpu_grad_function_inplace(const CPUContext& dev_ctx,
|
|
DenseTensor* grad_x,
|
|
const DenseTensor* mask,
|
|
float dropout_prob) {
|
|
DropoutHelper<T>(dev_ctx, grad_x, grad_x, mask, dropout_prob);
|
|
}
|
|
|
|
template <typename GradCellType,
|
|
template <typename, typename>
|
|
class SingleGradLayerT,
|
|
template <typename, typename>
|
|
class BidirGradLayerT,
|
|
typename T>
|
|
void RnnGradFunc(const CPUContext& dev_ctx,
|
|
const DenseTensor& x,
|
|
const std::vector<const DenseTensor*>& pre_state,
|
|
const std::vector<const DenseTensor*>& weight_list,
|
|
const optional<DenseTensor>& sequence_length,
|
|
const DenseTensor& out,
|
|
const DenseTensor& dropout_state,
|
|
const DenseTensor& reserve,
|
|
const DenseTensor& out_grad,
|
|
const std::vector<const DenseTensor*>& state_grad,
|
|
float dropout_prob,
|
|
bool is_bidirec,
|
|
int input_size UNUSED,
|
|
int hidden_size,
|
|
int num_layers,
|
|
const std::string& mode,
|
|
int seed UNUSED,
|
|
bool is_test,
|
|
int gate_num,
|
|
DenseTensor* x_grad,
|
|
std::vector<DenseTensor*> pre_state_grad,
|
|
std::vector<DenseTensor*> weight_grad_list) {
|
|
const DenseTensor* init_h = pre_state[0];
|
|
const DenseTensor* init_c = nullptr;
|
|
if (is_lstm(mode)) {
|
|
init_c = pre_state[1];
|
|
}
|
|
const DenseTensor* last_h_grad = state_grad[0];
|
|
const DenseTensor* last_c_grad = nullptr;
|
|
if (is_lstm(mode)) {
|
|
last_c_grad = state_grad[1];
|
|
}
|
|
|
|
DenseTensor* init_h_grad = nullptr;
|
|
DenseTensor* init_c_grad = nullptr;
|
|
if (!pre_state_grad.empty()) { // has gradient
|
|
init_h_grad = pre_state_grad[0];
|
|
if (is_lstm(mode) && pre_state_grad.size() > 1) {
|
|
init_c_grad = pre_state_grad[1];
|
|
}
|
|
}
|
|
|
|
// get the input_size, batch_size, time_step
|
|
const int time_step = static_cast<int>(x.dims()[0]);
|
|
const int batch_size = static_cast<int>(x.dims()[1]);
|
|
const int direction_num = is_bidirec ? 2 : 1;
|
|
|
|
// allocate the memory and initization the x_grad
|
|
DenseTensor x_grad_value;
|
|
if (!x_grad) {
|
|
x_grad = &x_grad_value;
|
|
}
|
|
x_grad->Resize(x.dims());
|
|
dev_ctx.Alloc<T>(x_grad);
|
|
|
|
if (init_h_grad) {
|
|
init_h_grad->Resize(init_h->dims());
|
|
dev_ctx.Alloc<T>(init_h_grad);
|
|
}
|
|
if (init_c_grad) {
|
|
init_c_grad->Resize(init_c->dims());
|
|
dev_ctx.Alloc<T>(init_c_grad);
|
|
}
|
|
|
|
// reset the parameter to sorted order and allocate the memory
|
|
std::vector<std::vector<DenseTensor>> parameter_lists;
|
|
parameter_lists.reserve(num_layers);
|
|
ResetParameterVector(
|
|
weight_list, num_layers, gate_num, is_bidirec, ¶meter_lists);
|
|
|
|
for (auto& weight_grad : weight_grad_list) {
|
|
dev_ctx.Alloc<T>(weight_grad);
|
|
}
|
|
std::vector<std::vector<DenseTensor>> parameter_lists_grad;
|
|
parameter_lists_grad.reserve(num_layers);
|
|
ResetParameterVector(weight_grad_list,
|
|
num_layers,
|
|
gate_num,
|
|
is_bidirec,
|
|
¶meter_lists_grad);
|
|
|
|
// resolve the state of reverse_state
|
|
DenseTensor gate_tensor;
|
|
DenseTensor state_tensor;
|
|
DenseTensor act_state_tensor;
|
|
DenseTensor hidden_tensor;
|
|
SplitReserveData(dev_ctx,
|
|
direction_num,
|
|
time_step,
|
|
batch_size,
|
|
hidden_size,
|
|
gate_num,
|
|
num_layers,
|
|
mode,
|
|
&reserve,
|
|
&gate_tensor,
|
|
&state_tensor,
|
|
&act_state_tensor,
|
|
&hidden_tensor);
|
|
int gate_num_tmp = gate_num;
|
|
if (gate_num == 0) {
|
|
gate_num_tmp = 1;
|
|
}
|
|
gate_tensor.Resize({num_layers,
|
|
time_step * direction_num,
|
|
batch_size,
|
|
hidden_size * gate_num_tmp});
|
|
if (state_tensor.numel() > 0) {
|
|
state_tensor.Resize(
|
|
{num_layers, time_step * direction_num, batch_size, hidden_size});
|
|
}
|
|
if (act_state_tensor.numel() > 0) {
|
|
act_state_tensor.Resize(
|
|
{num_layers, time_step * direction_num, batch_size, hidden_size});
|
|
}
|
|
if (num_layers > 1) {
|
|
hidden_tensor.Resize(
|
|
{num_layers - 1, time_step, batch_size, hidden_size * direction_num});
|
|
}
|
|
|
|
// unbind
|
|
auto last_h_grad_unbind = Unbind(*last_h_grad);
|
|
auto gate_tensor_unbind = Unbind(gate_tensor);
|
|
std::vector<DenseTensor> last_c_grad_unbind;
|
|
if (last_c_grad) {
|
|
last_c_grad_unbind = Unbind(*last_c_grad);
|
|
}
|
|
|
|
std::vector<DenseTensor> init_h_unbind, init_c_unbind;
|
|
std::vector<DenseTensor> init_h_grad_unbind, init_c_grad_unbind;
|
|
std::vector<DenseTensor> state_tensor_unbind, act_state_tensor_unbind;
|
|
std::vector<DenseTensor> hidden_tensor_unbind;
|
|
|
|
init_h_unbind = Unbind(*init_h);
|
|
if (init_c) {
|
|
init_c_unbind = Unbind(*init_c);
|
|
}
|
|
|
|
if (init_h_grad != nullptr) {
|
|
init_h_grad_unbind = Unbind(*init_h_grad);
|
|
}
|
|
if (init_c_grad != nullptr) {
|
|
init_c_grad_unbind = Unbind(*init_c_grad);
|
|
}
|
|
if (state_tensor.numel() > 0) {
|
|
state_tensor_unbind = Unbind(state_tensor);
|
|
}
|
|
if (act_state_tensor.numel() > 0) {
|
|
act_state_tensor_unbind = Unbind(act_state_tensor);
|
|
}
|
|
if (num_layers > 1) {
|
|
hidden_tensor_unbind = Unbind(hidden_tensor);
|
|
}
|
|
// squeeze the hidden first dim
|
|
for (auto& hidden_tensor : hidden_tensor_unbind) {
|
|
hidden_tensor.Resize(
|
|
slice_ddim(hidden_tensor.dims(), 1, hidden_tensor.dims().size()));
|
|
}
|
|
// add the output tensor to the hidden vector
|
|
DenseTensor tmp;
|
|
hidden_tensor_unbind.emplace_back(tmp);
|
|
hidden_tensor_unbind[num_layers - 1].ShareDataWith(out);
|
|
|
|
GradCellType cell;
|
|
DenseTensor layer_input;
|
|
DenseTensor layer_output;
|
|
DenseTensor* layer_x_grad_holder = nullptr;
|
|
DenseTensor tmp_out;
|
|
tmp_out.ShareDataWith(out_grad);
|
|
DenseTensor* layer_output_grad_holder = &tmp_out;
|
|
DenseTensor x_grad_temp;
|
|
DenseTensor output_grad_temp;
|
|
|
|
bool has_allocate_mem = false;
|
|
for (int i = num_layers - 1; i >= 0; --i) {
|
|
// the layer input output had saved, just use the data
|
|
if (i > 0) {
|
|
if (layer_input.numel() == 0) {
|
|
layer_input.Resize(hidden_tensor_unbind[i - 1].dims());
|
|
dev_ctx.Alloc<T>(&layer_input);
|
|
}
|
|
DropoutHelper<T>(dev_ctx,
|
|
&hidden_tensor_unbind[i - 1],
|
|
&layer_input,
|
|
&dropout_state,
|
|
dropout_prob);
|
|
} else {
|
|
layer_input.ShareDataWith(x);
|
|
}
|
|
layer_output.ShareDataWith(hidden_tensor_unbind[i]);
|
|
if (num_layers == 1) {
|
|
layer_x_grad_holder = x_grad;
|
|
} else {
|
|
if (i == num_layers - 1) {
|
|
x_grad_temp.Resize(layer_input.dims());
|
|
dev_ctx.Alloc<T>(&x_grad_temp);
|
|
layer_x_grad_holder = &x_grad_temp;
|
|
}
|
|
}
|
|
if (is_bidirec) {
|
|
BidirGradLayerT<T, GradCellType> layer(cell);
|
|
layer(dev_ctx,
|
|
&layer_input,
|
|
&layer_output,
|
|
&init_h_unbind,
|
|
&init_c_unbind,
|
|
last_h_grad_unbind,
|
|
last_c_grad_unbind,
|
|
gate_tensor_unbind,
|
|
state_tensor_unbind,
|
|
act_state_tensor_unbind,
|
|
layer_output_grad_holder,
|
|
parameter_lists,
|
|
sequence_length.get_ptr(),
|
|
layer_x_grad_holder,
|
|
&init_h_grad_unbind,
|
|
&init_c_grad_unbind,
|
|
¶meter_lists_grad,
|
|
i,
|
|
is_bidirec,
|
|
hidden_size,
|
|
mode,
|
|
gate_num_tmp);
|
|
} else {
|
|
SingleGradLayerT<T, GradCellType> layer(cell);
|
|
layer(dev_ctx,
|
|
&layer_input,
|
|
&layer_output,
|
|
&init_h_unbind,
|
|
&init_c_unbind,
|
|
last_h_grad_unbind,
|
|
last_c_grad_unbind,
|
|
gate_tensor_unbind,
|
|
state_tensor_unbind,
|
|
act_state_tensor_unbind,
|
|
layer_output_grad_holder,
|
|
parameter_lists,
|
|
sequence_length.get_ptr(),
|
|
layer_x_grad_holder,
|
|
&init_h_grad_unbind,
|
|
&init_c_grad_unbind,
|
|
¶meter_lists_grad,
|
|
i,
|
|
is_bidirec,
|
|
hidden_size,
|
|
mode,
|
|
gate_num_tmp);
|
|
}
|
|
|
|
// calculate the dropout gradient for the layer_x_grad_holder
|
|
// dropout_state save in the forward process
|
|
if (i > 0) {
|
|
if ((!is_test) && (dropout_prob != 0)) {
|
|
dropout_cpu_grad_function_inplace<T>(
|
|
dev_ctx, layer_x_grad_holder, &dropout_state, dropout_prob);
|
|
}
|
|
}
|
|
|
|
if (i - 1 == 0) {
|
|
layer_output_grad_holder = x_grad;
|
|
} else {
|
|
if (!has_allocate_mem) {
|
|
output_grad_temp.Resize(layer_x_grad_holder->dims());
|
|
dev_ctx.Alloc<T>(&output_grad_temp);
|
|
layer_output_grad_holder = &output_grad_temp;
|
|
has_allocate_mem = true;
|
|
}
|
|
}
|
|
SwapPointer(&layer_x_grad_holder, &layer_output_grad_holder);
|
|
}
|
|
}
|
|
|
|
template <typename T, typename Context>
|
|
void RnnGradKernel(const Context& dev_ctx,
|
|
const DenseTensor& x,
|
|
const std::vector<const DenseTensor*>& pre_state,
|
|
const std::vector<const DenseTensor*>& weight_list,
|
|
const optional<DenseTensor>& sequence_length,
|
|
const DenseTensor& out,
|
|
const DenseTensor& dropout_state,
|
|
const DenseTensor& reserve,
|
|
const DenseTensor& out_grad,
|
|
const std::vector<const DenseTensor*>& state_grad,
|
|
float dropout_prob,
|
|
bool is_bidirec,
|
|
int input_size,
|
|
int hidden_size,
|
|
int num_layers,
|
|
const std::string& mode,
|
|
int seed,
|
|
bool is_test,
|
|
DenseTensor* x_grad,
|
|
std::vector<DenseTensor*> pre_state_grad,
|
|
std::vector<DenseTensor*> weight_grad_list) {
|
|
int gate_num = 4;
|
|
if (is_lstm(mode)) {
|
|
RnnGradFunc<LSTMGradCell<T>, SingleGradLayer, BidirGradLayer, T>(
|
|
dev_ctx,
|
|
x,
|
|
pre_state,
|
|
weight_list,
|
|
sequence_length,
|
|
out,
|
|
dropout_state,
|
|
reserve,
|
|
out_grad,
|
|
state_grad,
|
|
dropout_prob,
|
|
is_bidirec,
|
|
input_size,
|
|
hidden_size,
|
|
num_layers,
|
|
mode,
|
|
seed,
|
|
is_test,
|
|
gate_num,
|
|
x_grad,
|
|
pre_state_grad,
|
|
weight_grad_list);
|
|
} else if (is_gru(mode)) {
|
|
gate_num = 3;
|
|
RnnGradFunc<GRUGradCell<T>, SingleGradLayer, BidirGradLayer, T>(
|
|
dev_ctx,
|
|
x,
|
|
pre_state,
|
|
weight_list,
|
|
sequence_length,
|
|
out,
|
|
dropout_state,
|
|
reserve,
|
|
out_grad,
|
|
state_grad,
|
|
dropout_prob,
|
|
is_bidirec,
|
|
input_size,
|
|
hidden_size,
|
|
num_layers,
|
|
mode,
|
|
seed,
|
|
is_test,
|
|
gate_num,
|
|
x_grad,
|
|
pre_state_grad,
|
|
weight_grad_list);
|
|
// run gru
|
|
} else if (is_rnn_relu(mode)) { // NOLINT
|
|
gate_num = 1;
|
|
RnnGradFunc<SimpleRNNGradCell<T, funcs::ReluGradFunctor>,
|
|
SingleGradLayer,
|
|
BidirGradLayer,
|
|
T>(dev_ctx,
|
|
x,
|
|
pre_state,
|
|
weight_list,
|
|
sequence_length,
|
|
out,
|
|
dropout_state,
|
|
reserve,
|
|
out_grad,
|
|
state_grad,
|
|
dropout_prob,
|
|
is_bidirec,
|
|
input_size,
|
|
hidden_size,
|
|
num_layers,
|
|
mode,
|
|
seed,
|
|
is_test,
|
|
gate_num,
|
|
x_grad,
|
|
pre_state_grad,
|
|
weight_grad_list);
|
|
// run rnn
|
|
} else if (is_rnn_tanh(mode)) {
|
|
gate_num = 1;
|
|
RnnGradFunc<SimpleRNNGradCell<T, funcs::TanhGradFunctor>,
|
|
SingleGradLayer,
|
|
BidirGradLayer,
|
|
T>(dev_ctx,
|
|
x,
|
|
pre_state,
|
|
weight_list,
|
|
sequence_length,
|
|
out,
|
|
dropout_state,
|
|
reserve,
|
|
out_grad,
|
|
state_grad,
|
|
dropout_prob,
|
|
is_bidirec,
|
|
input_size,
|
|
hidden_size,
|
|
num_layers,
|
|
mode,
|
|
seed,
|
|
is_test,
|
|
gate_num,
|
|
x_grad,
|
|
pre_state_grad,
|
|
weight_grad_list);
|
|
}
|
|
}
|
|
|
|
} // namespace phi
|
|
|
|
PD_REGISTER_KERNEL(
|
|
rnn_grad, CPU, ALL_LAYOUT, phi::RnnGradKernel, float, double) {}
|