src/lstm_op.cc (394 lines of code) (raw):
#include "tensorflow/core/framework/op.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/framework/shape_inference.h"
#include "tensorflow/core/framework/types.h"
#include "tensorflow/core/lib/core/status.h"
#include "tensorflow/core/platform/stream_executor.h"
#include "tensorflow/stream_executor/cuda/cuda_stream.h"
#include "gpu_types.h"
using namespace tensorflow;
using shape_inference::DimensionHandle;
using shape_inference::InferenceContext;
using shape_inference::ShapeHandle;
using perftools::gputools::cuda::CUDAStream;
template <typename T, typename V> bool LSTM_Gates_Forward(CUstream stream, T* c_next, T* h_next, const T* c_prev, const T* h_prev, const float* bias, float forget_bias, int N, int K);
template <typename T, typename V> bool LSTM4_Gates_Forward(CUstream stream, T* c_next, T* h_next, const T* c, const T* i, const T* f, const T* o, const T* u, float forget_bias, int N, int K);
template <typename B, typename F, typename VB, typename VF> bool LSTM_Gates_Backward(CUstream stream, B* dc, B* dh, const B* ec, const B* eh, const F* c_prev, const F* h_prev, const float* bias, int N, int K, float forget_bias);
template <typename B, typename A, typename VB, typename VA> bool LSTM4_Gates_Backward(CUstream stream, B* dc, B* di, B* df, B* doo, B* du, const B* ec, const B* eh, const A* c, const A* i, const A* f, const A* o, const A* u, int N, int K, float forget_bias);
template <typename T, typename V> bool Split4_Forward (CUstream stream, T* z0, T* z1, T* z2, T* z3, const T* x, int N, int K);
template <typename T, typename V> bool Concat4_Forward(CUstream stream, T* dx, const T* z0, const T* z1, const T* z2, const T* z3, int N, int K);
template <typename T, typename V> bool SparseReluForward(CUstream stream, T* y, const T* x, float alpha, uint K, uint N);
Status UnchangedShape(shape_inference::InferenceContext* ctx);
REGISTER_OP("LSTMGates")
.Input("c_prev: T")
.Input("h_prev: T")
.Input("bias: nbias * float")
.Output("c_next: T")
.Output("h_next: T")
.Attr("T: {half, float, bfloat16}")
.Attr("forget_bias: float = 1.0")
.Attr("nbias: int >= 0")
.SetShapeFn([](InferenceContext* ctx) {
ctx->set_output(0, ctx->input(0));
ctx->set_output(1, ctx->input(0));
return Status::OK();
})
.Doc(R"doc(
i, u, f, o = split(z, 4, 1)
i = sigmoid(i)
u = tanh(u)
f = sigmoid(f + forget_bias)
o = sigmoid(o)
c = add(multiply(f, c), multiply(i, u))
h = multiply(o, tanh(c))
)doc");
template <typename T, typename V1, typename V4>
class LSTMGatesOp : public OpKernel {
public:
explicit LSTMGatesOp(OpKernelConstruction* ctx) : OpKernel(ctx) {
OP_REQUIRES_OK(ctx, ctx->GetAttr("forget_bias", &forget_bias_));
}
void Compute(OpKernelContext* ctx) override
{
const Tensor& c_prev = ctx->input(0);
const Tensor& h_prev = ctx->input(1);
int N = h_prev.dim_size(0);
int K = h_prev.dim_size(1);
OpInputList bias;
ctx->input_list("bias", &bias);
const float* Bias = bias.size() > 0 ? bias[0].flat<float>().data() : NULL;
Tensor* c_next = nullptr;
Tensor* h_next = nullptr;
OP_REQUIRES_OK(ctx, ctx->allocate_output(0, c_prev.shape(), &c_next));
OP_REQUIRES_OK(ctx, ctx->allocate_output(1, c_prev.shape(), &h_next));
V1* c_next_ptr = ( V1*)c_next->flat<T>().data();
V1* h_next_ptr = ( V1*)h_next->flat<T>().data();
const V1* c_prev_ptr = (const V1*)c_prev.flat<T>().data();
const V1* h_prev_ptr = (const V1*)h_prev.flat<T>().data();
CUstream stream = ((CUDAStream*)ctx->op_device_context()->stream()->implementation())->cuda_stream();
LSTM_Gates_Forward<V1,V4>(stream, c_next_ptr, h_next_ptr, c_prev_ptr, h_prev_ptr, Bias, forget_bias_, N, K);
}
float forget_bias_;
};
REGISTER_KERNEL_BUILDER(Name("LSTMGates").Device(DEVICE_GPU).TypeConstraint<float>("T"),LSTMGatesOp<float,float,float4>);
REGISTER_KERNEL_BUILDER(Name("LSTMGates").Device(DEVICE_GPU).TypeConstraint<EHALF>("T"),LSTMGatesOp<EHALF,ehalf,ehalf4>);
REGISTER_KERNEL_BUILDER(Name("LSTMGates").Device(DEVICE_GPU).TypeConstraint<BHALF>("T"),LSTMGatesOp<BHALF,bhalf,bhalf4>);
REGISTER_OP("LSTMGates4")
.Input("c: T")
.Input("i: T")
.Input("u: T")
.Input("f: T")
.Input("o: T")
.Output("c_next: T")
.Output("h_next: T")
.Attr("T: {half, float, bfloat16}")
.Attr("forget_bias: float = 1.0")
.SetShapeFn([](InferenceContext* ctx) {
ctx->set_output(0, ctx->input(0));
ctx->set_output(1, ctx->input(0));
return Status::OK();
})
.Doc(R"doc(
i = sigmoid(i)
u = tanh(u)
f = sigmoid(f + forget_bias)
o = sigmoid(o)
c = add(multiply(f, c), multiply(i, u))
h = multiply(o, tanh(c))
)doc");
template <typename T, typename V1, typename V4 >
class LSTMGates4Op : public OpKernel {
public:
explicit LSTMGates4Op(OpKernelConstruction* ctx) : OpKernel(ctx) {
OP_REQUIRES_OK(ctx, ctx->GetAttr("forget_bias", &forget_bias_));
}
void Compute(OpKernelContext* ctx) override {
const Tensor& c = ctx->input(0);
const Tensor& i = ctx->input(1);
const Tensor& u = ctx->input(2);
const Tensor& f = ctx->input(3);
const Tensor& o = ctx->input(4);
int N = c.dim_size(0);
int K = c.dim_size(1);
Tensor* c_next = nullptr;
Tensor* h_next = nullptr;
OP_REQUIRES_OK(ctx, ctx->allocate_output(0, c.shape(), &c_next));
OP_REQUIRES_OK(ctx, ctx->allocate_output(1, c.shape(), &h_next));
V1* c_next_ptr = (V1*)c_next->flat<T>().data();
V1* h_next_ptr = (V1*)h_next->flat<T>().data();
const V1* c_ptr = (const V1*)c.flat<T>().data();
const V1* i_ptr = (const V1*)i.flat<T>().data();
const V1* f_ptr = (const V1*)f.flat<T>().data();
const V1* o_ptr = (const V1*)o.flat<T>().data();
const V1* u_ptr = (const V1*)u.flat<T>().data();
CUstream stream = ((CUDAStream*)ctx->op_device_context()->stream()->implementation())->cuda_stream();
LSTM4_Gates_Forward<V1,V4>(stream, c_next_ptr, h_next_ptr, c_ptr, i_ptr, f_ptr, o_ptr, u_ptr, forget_bias_, N, K);
}
float forget_bias_;
};
REGISTER_KERNEL_BUILDER(Name("LSTMGates4").Device(DEVICE_GPU).TypeConstraint<float>("T"),LSTMGates4Op<float,float,float4>);
REGISTER_KERNEL_BUILDER(Name("LSTMGates4").Device(DEVICE_GPU).TypeConstraint<EHALF>("T"),LSTMGates4Op<EHALF,ehalf,ehalf4>);
REGISTER_KERNEL_BUILDER(Name("LSTMGates4").Device(DEVICE_GPU).TypeConstraint<BHALF>("T"),LSTMGates4Op<BHALF,bhalf,bhalf4>);
REGISTER_OP("LSTMGatesGrad")
.Input("c_prev: F")
.Input("h_prev: F")
.Input("bias: nbias * float")
.Input("grads: ngrads * B")
.Output("dc: B")
.Output("dh: B")
.Attr("B: {half, float, bfloat16}")
.Attr("F: {half, float, bfloat16}")
.Attr("forget_bias: float = 1.0")
.Attr("nbias: int >= 0")
.Attr("ngrads: int >= 1")
.SetShapeFn([](InferenceContext* ctx) {
ctx->set_output(0, ctx->input(0));
ctx->set_output(1, ctx->input(1));
return Status::OK();
})
.Doc(R"doc(
di = ec * tan_u * sig_grad(sig_i)
df = ec * c * sig_grad(sig_f)
do = eh * c_act * sig_grad(sig_o)
du = ec * sig_i * tanh_grad(tan_u)
dc = (ec + eh * sig_o * tanh_grad(c_act)) * sig_f
dh = concat([di,df,do,du], 1)
)doc");
template <typename B, typename F, typename VB1, typename VF1, typename VB4, typename VF4>
class LSTMGatesGradOp : public OpKernel {
public:
explicit LSTMGatesGradOp(OpKernelConstruction* ctx) : OpKernel(ctx) {
OP_REQUIRES_OK(ctx, ctx->GetAttr("forget_bias", &forget_bias_));
}
void Compute(OpKernelContext* ctx) override
{
const Tensor& c_prev = ctx->input(0);
const Tensor& h_prev = ctx->input(1);
OpInputList bias, grads;
ctx->input_list("bias", &bias);
ctx->input_list("grads", &grads);
int N = h_prev.dim_size(0);
int K = h_prev.dim_size(1);
Tensor* dc = nullptr;
Tensor* dh = nullptr;
OP_REQUIRES_OK(ctx, ctx->allocate_output(0, c_prev.shape(), &dc));
OP_REQUIRES_OK(ctx, ctx->allocate_output(1, h_prev.shape(), &dh));
VB1* dc_ptr = (VB1*)dc->flat<B>().data();
VB1* dh_ptr = (VB1*)dh->flat<B>().data();
const VF1* c_prev_ptr = (const VF1*)c_prev.flat<F>().data();
const VF1* h_prev_ptr = (const VF1*)h_prev.flat<F>().data();
const VB1* eh_ptr = (const VB1*)grads[0].flat<B>().data();
const VB1* ec_ptr = grads.size() > 1 ? (const VB1*)grads[1].flat<B>().data() : nullptr;
const float* bias_ptr = bias.size() > 0 ? bias[0].flat<float>().data() : nullptr;
CUstream stream = ((CUDAStream*)ctx->op_device_context()->stream()->implementation())->cuda_stream();
LSTM_Gates_Backward<VB1,VF1,VB4,VF4>(stream, dc_ptr, dh_ptr, ec_ptr, eh_ptr, c_prev_ptr, h_prev_ptr, bias_ptr, N, K, forget_bias_);
}
float forget_bias_;
};
REGISTER_KERNEL_BUILDER(Name("LSTMGatesGrad").Device(DEVICE_GPU).TypeConstraint<float>("B").TypeConstraint<float>("F"),LSTMGatesGradOp<float,float,float,float,float4,float4>);
REGISTER_KERNEL_BUILDER(Name("LSTMGatesGrad").Device(DEVICE_GPU).TypeConstraint<EHALF>("B").TypeConstraint<EHALF>("F"),LSTMGatesGradOp<EHALF,EHALF,ehalf,ehalf,ehalf4,ehalf4>);
//REGISTER_KERNEL_BUILDER(Name("LSTMGatesGrad").Device(DEVICE_GPU).TypeConstraint<float>("B").TypeConstraint<EHALF>("F"),LSTMGatesGradOp<float,EHALF,float,ehalf,float4,ehalf4>);
REGISTER_KERNEL_BUILDER(Name("LSTMGatesGrad").Device(DEVICE_GPU).TypeConstraint<BHALF>("B").TypeConstraint<BHALF>("F"),LSTMGatesGradOp<BHALF,BHALF,bhalf,bhalf,bhalf4,bhalf4>);
//REGISTER_KERNEL_BUILDER(Name("LSTMGatesGrad").Device(DEVICE_GPU).TypeConstraint<float>("B").TypeConstraint<BHALF>("F"),LSTMGatesGradOp<float,BHALF,float,bhalf,float4,bhalf4>);
REGISTER_OP("LSTMGates4Grad")
.Input("c: F")
.Input("i: F")
.Input("u: F")
.Input("f: F")
.Input("o: F")
.Input("grads: ngrads * B")
.Output("dc: B")
.Output("di: B")
.Output("du: B")
.Output("df: B")
.Output("do: B")
.Attr("B: {half, float, bfloat16}")
.Attr("F: {half, float, bfloat16}")
.Attr("forget_bias: float = 1.0")
.Attr("ngrads: int >= 1")
.SetShapeFn([](InferenceContext* ctx) {
ctx->set_output(0, ctx->input(0));
ctx->set_output(1, ctx->input(0));
ctx->set_output(2, ctx->input(0));
ctx->set_output(3, ctx->input(0));
ctx->set_output(4, ctx->input(0));
return Status::OK();
})
.Doc(R"doc(
di = ec * tan_u * sig_grad(sig_i)
df = ec * c * sig_grad(sig_f)
do = eh * c_act * sig_grad(sig_o)
du = ec * sig_i * tanh_grad(tan_u)
dc = (ec + eh * sig_o * tanh_grad(c_act)) * sig_f
)doc");
template <typename B, typename F, typename VB1, typename VF1, typename VB4, typename VF4>
class LSTMGates4GradOp : public OpKernel {
public:
explicit LSTMGates4GradOp(OpKernelConstruction* ctx) : OpKernel(ctx) {
OP_REQUIRES_OK(ctx, ctx->GetAttr("forget_bias", &forget_bias_));
}
void Compute(OpKernelContext* ctx) override {
const Tensor& c = ctx->input(0);
const Tensor& i = ctx->input(1);
const Tensor& u = ctx->input(2);
const Tensor& f = ctx->input(3);
const Tensor& o = ctx->input(4);
const Tensor& eh = ctx->input(5);
int N = c.dim_size(0);
int K = c.dim_size(1);
Tensor* dc = nullptr;
Tensor* di = nullptr;
Tensor* df = nullptr;
Tensor* doo = nullptr;
Tensor* du = nullptr;
OP_REQUIRES_OK(ctx, ctx->allocate_output(0, c.shape(), &dc));
OP_REQUIRES_OK(ctx, ctx->allocate_output(1, c.shape(), &di));
OP_REQUIRES_OK(ctx, ctx->allocate_output(2, c.shape(), &du));
OP_REQUIRES_OK(ctx, ctx->allocate_output(3, c.shape(), &df));
OP_REQUIRES_OK(ctx, ctx->allocate_output(4, c.shape(), &doo));
VB1* dc_ptr = (VB1*)dc->flat<B>().data();
VB1* di_ptr = (VB1*)di->flat<B>().data();
VB1* df_ptr = (VB1*)df->flat<B>().data();
VB1* do_ptr = (VB1*)doo->flat<B>().data();
VB1* du_ptr = (VB1*)du->flat<B>().data();
const VF1* c_ptr = (const VF1*)c.flat<F>().data();
const VF1* i_ptr = (const VF1*)i.flat<F>().data();
const VF1* f_ptr = (const VF1*)f.flat<F>().data();
const VF1* o_ptr = (const VF1*)o.flat<F>().data();
const VF1* u_ptr = (const VF1*)u.flat<F>().data();
const VB1* eh_ptr = (const VB1*)eh.flat<B>().data();
const VB1* ec_ptr = nullptr;
if (ctx->num_inputs() == 7)
ec_ptr = (const VB1*)ctx->input(6).flat<B>().data();
CUstream stream = ((CUDAStream*)ctx->op_device_context()->stream()->implementation())->cuda_stream();
LSTM4_Gates_Backward<VB1,VF1,VB4,VF4>(stream, dc_ptr, di_ptr, df_ptr, do_ptr, du_ptr, ec_ptr, eh_ptr, c_ptr, i_ptr, f_ptr, o_ptr, u_ptr, N, K, forget_bias_);
}
float forget_bias_;
};
REGISTER_KERNEL_BUILDER(Name("LSTMGates4Grad").Device(DEVICE_GPU).TypeConstraint<float>("B").TypeConstraint<float>("F"),LSTMGates4GradOp<float,float,float,float,float4,float4>);
REGISTER_KERNEL_BUILDER(Name("LSTMGates4Grad").Device(DEVICE_GPU).TypeConstraint<EHALF>("B").TypeConstraint<EHALF>("F"),LSTMGates4GradOp<EHALF,EHALF,ehalf,ehalf,ehalf4,ehalf4>);
//REGISTER_KERNEL_BUILDER(Name("LSTMGates4Grad").Device(DEVICE_GPU).TypeConstraint<float>("B").TypeConstraint<EHALF>("F"),LSTMGates4GradOp<float,EHALF,float,ehalf,float4,ehalf4>);
REGISTER_KERNEL_BUILDER(Name("LSTMGates4Grad").Device(DEVICE_GPU).TypeConstraint<BHALF>("B").TypeConstraint<BHALF>("F"),LSTMGates4GradOp<BHALF,BHALF,bhalf,bhalf,bhalf4,bhalf4>);
//REGISTER_KERNEL_BUILDER(Name("LSTMGates4Grad").Device(DEVICE_GPU).TypeConstraint<float>("B").TypeConstraint<BHALF>("F"),LSTMGates4GradOp<float,BHALF,float,bhalf,float4,bhalf4>);
REGISTER_OP("Split4")
.Input("x: T")
.Output("z0: T")
.Output("z1: T")
.Output("z2: T")
.Output("z3: T")
.Attr("T: {half, float, bfloat16}")
.SetShapeFn([](InferenceContext* ctx) {
ShapeHandle x = ctx->input(0);
DimensionHandle N = ctx->Dim(x,0);
DimensionHandle K;
TF_RETURN_IF_ERROR(ctx->Divide( ctx->Dim(x,1), 4, true, &K));
ShapeHandle z = ctx->MakeShape({ N, K });
ctx->set_output(0, z);
ctx->set_output(1, z);
ctx->set_output(2, z);
ctx->set_output(3, z);
return Status::OK();
})
.Doc(R"doc(
split 4 for lstm type nets
)doc");
template <typename T, typename V1, typename V4>
class Split4Op : public OpKernel {
public:
explicit Split4Op(OpKernelConstruction* ctx) : OpKernel(ctx) {}
void Compute(OpKernelContext* ctx) override {
const Tensor& x = ctx->input(0);
int N = x.dim_size(0);
int K = x.dim_size(1);
TensorShape z_shape({N, K >> 2});
Tensor* z0 = nullptr;
Tensor* z1 = nullptr;
Tensor* z2 = nullptr;
Tensor* z3 = nullptr;
OP_REQUIRES_OK(ctx, ctx->allocate_output(0, z_shape, &z0));
OP_REQUIRES_OK(ctx, ctx->allocate_output(1, z_shape, &z1));
OP_REQUIRES_OK(ctx, ctx->allocate_output(2, z_shape, &z2));
OP_REQUIRES_OK(ctx, ctx->allocate_output(3, z_shape, &z3));
V1* z0_ptr = (V1*)z0->flat<T>().data();
V1* z1_ptr = (V1*)z1->flat<T>().data();
V1* z2_ptr = (V1*)z2->flat<T>().data();
V1* z3_ptr = (V1*)z3->flat<T>().data();
const V1* x_ptr = (const V1*)x.flat<T>().data();
CUstream stream = ((CUDAStream*)ctx->op_device_context()->stream()->implementation())->cuda_stream();
Split4_Forward<V1,V4>(stream, z0_ptr, z1_ptr, z2_ptr, z3_ptr, x_ptr, N, K);
}
};
REGISTER_KERNEL_BUILDER(Name("Split4").Device(DEVICE_GPU).TypeConstraint<float>("T"),Split4Op<float,float,float4>);
REGISTER_KERNEL_BUILDER(Name("Split4").Device(DEVICE_GPU).TypeConstraint<EHALF>("T"),Split4Op<EHALF,ehalf,ehalf4>);
REGISTER_KERNEL_BUILDER(Name("Split4").Device(DEVICE_GPU).TypeConstraint<BHALF>("T"),Split4Op<BHALF,bhalf,bhalf4>);
REGISTER_OP("Concat4")
.Input("dz0: T")
.Input("dz1: T")
.Input("dz2: T")
.Input("dz3: T")
.Output("dx: T")
.Attr("T: {half, float, bfloat16}")
.SetShapeFn([](InferenceContext* ctx) {
ShapeHandle dz = ctx->input(0);
DimensionHandle N = ctx->Dim(dz,0);
DimensionHandle K;
TF_RETURN_IF_ERROR(ctx->Multiply( ctx->Dim(dz,1), 4, &K));
ShapeHandle dx = ctx->MakeShape({ N, K });
ctx->set_output(0, dx);
return Status::OK();
})
.Doc(R"doc(
split 4 grad for lstm type nets
)doc");
template <typename T, typename V1, typename V4>
class Concat4Op : public OpKernel {
public:
explicit Concat4Op(OpKernelConstruction* ctx) : OpKernel(ctx) {}
void Compute(OpKernelContext* ctx) override {
const Tensor& dz0 = ctx->input(0);
const Tensor& dz1 = ctx->input(1);
const Tensor& dz2 = ctx->input(2);
const Tensor& dz3 = ctx->input(3);
int N = dz0.dim_size(0);
int K = dz0.dim_size(1) << 2;
Tensor* dx = nullptr;
OP_REQUIRES_OK(ctx, ctx->allocate_output(0, TensorShape({N, K}), &dx));
V1* dx_ptr = (V1*)dx->flat<T>().data();
const V1* dz0_ptr = (const V1*)dz0.flat<T>().data();
const V1* dz1_ptr = (const V1*)dz1.flat<T>().data();
const V1* dz2_ptr = (const V1*)dz2.flat<T>().data();
const V1* dz3_ptr = (const V1*)dz3.flat<T>().data();
CUstream stream = ((CUDAStream*)ctx->op_device_context()->stream()->implementation())->cuda_stream();
Concat4_Forward<V1,V4>(stream, dx_ptr, dz0_ptr, dz1_ptr, dz2_ptr, dz3_ptr, N, K);
}
};
REGISTER_KERNEL_BUILDER(Name("Concat4").Device(DEVICE_GPU).TypeConstraint<float>("T"),Concat4Op<float,float,float4>);
REGISTER_KERNEL_BUILDER(Name("Concat4").Device(DEVICE_GPU).TypeConstraint<EHALF>("T"),Concat4Op<EHALF,ehalf,ehalf4>);
REGISTER_KERNEL_BUILDER(Name("Concat4").Device(DEVICE_GPU).TypeConstraint<BHALF>("T"),Concat4Op<BHALF,bhalf,bhalf4>);
REGISTER_OP("SparseRelu")
.Input("x: T")
.Input("alpha: float")
.Output("z: T")
.Attr("T: {half, float, bfloat16}")
.SetShapeFn(UnchangedShape)
.Doc(R"doc(
sparse relu computed from mean/std
)doc");
template <typename T, typename V1, typename V4>
class SparseReluOp : public OpKernel {
public:
explicit SparseReluOp(OpKernelConstruction* ctx) : OpKernel(ctx) {}
void Compute(OpKernelContext* ctx) override {
const Tensor& x = ctx->input(0);
const Tensor& alpha = ctx->input(1);
uint rank = x.dims();
uint K = x.dim_size(--rank);
uint N = 1;
while (rank > 0) N *= x.dim_size(--rank);
Tensor* z = nullptr;
OP_REQUIRES_OK(ctx, ctx->allocate_output(0, x.shape(), &z));
V1* z_ptr = (V1*)z->flat<T>().data();
const V1* x_ptr = (const V1*)x.flat<T>().data();
CUstream stream = ((CUDAStream*)ctx->op_device_context()->stream()->implementation())->cuda_stream();
SparseReluForward<V1,V4>(stream, z_ptr, x_ptr, alpha.scalar<float>()(), K, N);
}
};
REGISTER_KERNEL_BUILDER(Name("SparseRelu").Device(DEVICE_GPU).TypeConstraint<float>("T").HostMemory("alpha"),SparseReluOp<float,float,float4>);
REGISTER_KERNEL_BUILDER(Name("SparseRelu").Device(DEVICE_GPU).TypeConstraint<EHALF>("T").HostMemory("alpha"),SparseReluOp<EHALF,ehalf,ehalf4>);
REGISTER_KERNEL_BUILDER(Name("SparseRelu").Device(DEVICE_GPU).TypeConstraint<BHALF>("T").HostMemory("alpha"),SparseReluOp<BHALF,bhalf,bhalf4>);