Skip to content

Commit cbe4292

Browse files
author
Yibing Liu
committed
Add sequence unpad op
test=develop
1 parent 5f2e837 commit cbe4292

File tree

4 files changed

+362
-0
lines changed

4 files changed

+362
-0
lines changed
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/* Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License. */
14+
15+
#include "paddle/fluid/operators/sequence_unpad_op.h"
16+
17+
namespace paddle {
18+
namespace operators {
19+
20+
class SequenceUnpadOp : public framework::OperatorWithKernel {
21+
public:
22+
using framework::OperatorWithKernel::OperatorWithKernel;
23+
24+
protected:
25+
void InferShape(framework::InferShapeContext* ctx) const override {
26+
PADDLE_ENFORCE(ctx->HasInput("X"),
27+
"Input(X) of SequenceUnpadOp should not be null.");
28+
PADDLE_ENFORCE(ctx->HasInput("Length"),
29+
"Input(Length) of SequenceUnpadOp should not be null.");
30+
PADDLE_ENFORCE(ctx->HasOutput("Out"),
31+
"Output(Out) of SequenceUnpadOp should not be null.");
32+
33+
auto x_dims = ctx->GetInputDim("X");
34+
PADDLE_ENFORCE_GE(x_dims.size(), 2,
35+
"The rank of Input(X) can't be less than 2.");
36+
37+
auto len_dims = ctx->GetInputDim("Length");
38+
PADDLE_ENFORCE(len_dims.size() == 2 && len_dims[1] == 1,
39+
"The shape of Input(Length) should be [batch_size, 1].");
40+
PADDLE_ENFORCE(
41+
len_dims[0] == x_dims[0],
42+
"Input(X) and Input(Length) should have the same first dimension.");
43+
44+
int64_t out_dim_0 = -1;
45+
if (ctx->IsRuntime()) {
46+
out_dim_0 = x_dims[0] * x_dims[1];
47+
}
48+
49+
std::vector<int64_t> out_dims_vec{out_dim_0};
50+
if (x_dims.size() == 2) {
51+
out_dims_vec.push_back(1);
52+
} else {
53+
for (size_t i = 2; i < x_dims.size(); ++i) {
54+
out_dims_vec.push_back(x_dims[i]);
55+
}
56+
}
57+
ctx->SetOutputDim("Out", framework::make_ddim(out_dims_vec));
58+
}
59+
60+
protected:
61+
framework::OpKernelType GetExpectedKernelType(
62+
const framework::ExecutionContext& ctx) const override {
63+
auto data_type = framework::GetDataTypeOfVar(ctx.InputVar("X"));
64+
return framework::OpKernelType(data_type, ctx.device_context());
65+
}
66+
};
67+
68+
class SequenceUnpadOpMaker : public framework::OpProtoAndCheckerMaker {
69+
public:
70+
void Make() override {
71+
AddInput("X",
72+
"(LoDTensor, default LoDTensor<float>) Input tensor which "
73+
"contains the padded sequences with equal length.");
74+
AddInput("Length",
75+
"(LoDTensor) The input tensor which specifies the actual ength of "
76+
"sequences after unpadding.");
77+
AddOutput(
78+
"Out",
79+
"(LoDTensor) The output tensor which contains unpadded sequences.");
80+
AddComment(R"DOC(
81+
Sequence Unpad Operator
82+
83+
This operator removes the padding data in the input sequences and convert
84+
them into sequences with actual length as output, identitied by lod
85+
information.
86+
87+
Example:
88+
89+
Given input tensor Input(X):
90+
X.data = [[ 1.0, 2.0, 3.0, 4.0, 5.0],
91+
[ 6.0, 7.0, 8.0, 9.0, 10.0],
92+
[11.0, 12.0, 13.0, 14.0, 15.0]],
93+
`
94+
in which there are 3 sequences padded to length 5, and the acutal length
95+
specified by Input(Length):
96+
97+
Length.data = [[2], [3], [4]],
98+
99+
after unpadding, Output(Out) will be:
100+
101+
Out.data = [[1.0, 2.0, 6.0, 7.0, 8.0, 11.0, 12.0, 13.0, 14.0]]
102+
Out.lod = [[0, 2, 5, 9]]
103+
104+
)DOC");
105+
}
106+
};
107+
108+
class SequenceUnpadGradOp : public framework::OperatorWithKernel {
109+
public:
110+
using framework::OperatorWithKernel::OperatorWithKernel;
111+
112+
void InferShape(framework::InferShapeContext* ctx) const override {
113+
PADDLE_ENFORCE(ctx->HasInput("X"),
114+
"Input(X) of SequenceUnpadGradOp should not be null.");
115+
PADDLE_ENFORCE(
116+
ctx->HasInput(framework::GradVarName("Out")),
117+
"Input(Out@GRAD) of SequenceUnpadGradOp should not be null.");
118+
119+
if (ctx->HasOutput(framework::GradVarName("X"))) {
120+
ctx->SetOutputDim(framework::GradVarName("X"), ctx->GetInputDim("X"));
121+
ctx->ShareLoD("X", /*->*/ framework::GradVarName("X"));
122+
}
123+
}
124+
125+
protected:
126+
framework::OpKernelType GetExpectedKernelType(
127+
const framework::ExecutionContext& ctx) const override {
128+
auto data_type = framework::GetDataTypeOfVar(ctx.InputVar("X"));
129+
return framework::OpKernelType(data_type, ctx.device_context());
130+
}
131+
};
132+
133+
} // namespace operators
134+
} // namespace paddle
135+
136+
namespace ops = paddle::operators;
137+
REGISTER_OPERATOR(sequence_unpad, ops::SequenceUnpadOp,
138+
ops::SequenceUnpadOpMaker,
139+
paddle::framework::DefaultGradOpDescMaker<true>);
140+
REGISTER_OPERATOR(sequence_unpad_grad, ops::SequenceUnpadGradOp);
141+
REGISTER_OP_CPU_KERNEL(
142+
sequence_unpad,
143+
ops::SequenceUnpadOpKernel<paddle::platform::CPUDeviceContext, float>,
144+
ops::SequenceUnpadOpKernel<paddle::platform::CPUDeviceContext, double>,
145+
ops::SequenceUnpadOpKernel<paddle::platform::CPUDeviceContext, int>,
146+
ops::SequenceUnpadOpKernel<paddle::platform::CPUDeviceContext, int64_t>);
147+
REGISTER_OP_CPU_KERNEL(
148+
sequence_unpad_grad,
149+
ops::SequenceUnpadGradOpKernel<paddle::platform::CPUDeviceContext, float>,
150+
ops::SequenceUnpadGradOpKernel<paddle::platform::CPUDeviceContext, double>,
151+
ops::SequenceUnpadGradOpKernel<paddle::platform::CPUDeviceContext, int>,
152+
ops::SequenceUnpadGradOpKernel<paddle::platform::CPUDeviceContext,
153+
int64_t>);
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/* Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License. */
14+
15+
#include "paddle/fluid/operators/sequence_unpad_op.h"
16+
17+
namespace ops = paddle::operators;
18+
REGISTER_OP_CUDA_KERNEL(
19+
sequence_unpad,
20+
ops::SequenceUnpadOpKernel<paddle::platform::CUDADeviceContext, float>,
21+
ops::SequenceUnpadOpKernel<paddle::platform::CUDADeviceContext, double>,
22+
ops::SequenceUnpadOpKernel<paddle::platform::CUDADeviceContext, int>,
23+
ops::SequenceUnpadOpKernel<paddle::platform::CUDADeviceContext, int64_t>);
24+
REGISTER_OP_CUDA_KERNEL(
25+
sequence_unpad_grad,
26+
ops::SequenceUnpadGradOpKernel<paddle::platform::CUDADeviceContext, float>,
27+
ops::SequenceUnpadGradOpKernel<paddle::platform::CUDADeviceContext, double>,
28+
ops::SequenceUnpadGradOpKernel<paddle::platform::CUDADeviceContext, int>,
29+
ops::SequenceUnpadGradOpKernel<paddle::platform::CUDADeviceContext,
30+
int64_t>);
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/* Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License. */
14+
15+
#pragma once
16+
17+
#include <vector>
18+
#include "paddle/fluid/framework/op_registry.h"
19+
#include "paddle/fluid/memory/memcpy.h"
20+
#include "paddle/fluid/operators/math/math_function.h"
21+
#include "paddle/fluid/operators/math/sequence_padding.h"
22+
23+
namespace paddle {
24+
namespace operators {
25+
26+
using LoDTensor = framework::LoDTensor;
27+
using LoD = framework::LoD;
28+
29+
template <typename DeviceContext, typename T>
30+
class SequenceUnpadOpKernel : public framework::OpKernel<T> {
31+
public:
32+
void Compute(const framework::ExecutionContext& ctx) const override {
33+
auto* x_t = ctx.Input<LoDTensor>("X");
34+
auto* len_t = ctx.Input<LoDTensor>("Length");
35+
auto* out_t = ctx.Output<LoDTensor>("Out");
36+
out_t->mutable_data<T>(ctx.GetPlace());
37+
38+
const int64_t* seq_len_ptr = nullptr;
39+
if (platform::is_gpu_place(ctx.GetPlace())) {
40+
LoDTensor seq_len_cpu;
41+
seq_len_cpu.Resize(len_t->dims());
42+
seq_len_ptr = seq_len_cpu.mutable_data<int64_t>(platform::CPUPlace());
43+
framework::TensorCopy(*len_t, platform::CPUPlace(),
44+
ctx.template device_context<DeviceContext>(),
45+
&seq_len_cpu);
46+
} else {
47+
seq_len_ptr = len_t->data<int64_t>();
48+
}
49+
50+
size_t batch_size = x_t->dims()[0];
51+
std::vector<size_t> out_lod0(batch_size + 1, 0);
52+
for (size_t i = 0; i < batch_size; ++i) {
53+
out_lod0[i + 1] = out_lod0[i] + seq_len_ptr[i];
54+
}
55+
56+
framework::LoD out_lod;
57+
out_lod.push_back(out_lod0);
58+
out_t->set_lod(out_lod);
59+
60+
std::vector<int64_t> out_dims_vec{static_cast<int64_t>(out_lod0.back())};
61+
if (x_t->dims().size() == 2) {
62+
out_dims_vec.push_back(1);
63+
} else {
64+
for (size_t i = 2; i < x_t->dims().size(); ++i) {
65+
out_dims_vec.push_back(x_t->dims()[i]);
66+
}
67+
}
68+
out_t->Resize(framework::make_ddim(out_dims_vec));
69+
70+
int64_t padded_length = x_t->dims()[1];
71+
math::UnpaddingLoDTensorFunctor<DeviceContext, T>()(
72+
ctx.template device_context<DeviceContext>(), *x_t, out_t,
73+
padded_length, 0, false, math::kBatchLengthWidth);
74+
}
75+
};
76+
77+
template <typename DeviceContext, typename T>
78+
class SequenceUnpadGradOpKernel : public framework::OpKernel<T> {
79+
public:
80+
void Compute(const framework::ExecutionContext& ctx) const override {
81+
auto* d_x = ctx.Output<LoDTensor>(framework::GradVarName("X"));
82+
if (d_x) {
83+
const auto* d_out = ctx.Input<LoDTensor>(framework::GradVarName("Out"));
84+
const auto* x_t = ctx.Input<LoDTensor>("X");
85+
d_x->mutable_data<T>(ctx.GetPlace());
86+
87+
int padded_length = x_t->dims()[1];
88+
89+
LoDTensor zero_pads;
90+
zero_pads.Resize({1, 1});
91+
zero_pads.mutable_data<T>(ctx.GetPlace());
92+
math::SetConstant<DeviceContext, T> set_zero;
93+
auto& dev_ctx = ctx.template device_context<DeviceContext>();
94+
set_zero(dev_ctx, &zero_pads, static_cast<T>(0));
95+
96+
math::PaddingLoDTensorFunctor<DeviceContext, T>()(
97+
ctx.template device_context<DeviceContext>(), *d_out, d_x, zero_pads,
98+
padded_length, 0, false, math::kBatchLengthWidth);
99+
}
100+
}
101+
};
102+
103+
} // namespace operators
104+
} // namespace paddle
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import unittest
16+
import six
17+
import numpy as np
18+
from op_test import OpTest
19+
20+
21+
class TestSequenceUnpadOp(OpTest):
22+
def init(self):
23+
self.length = [2, 3, 4]
24+
self.x_shape = (3, 5)
25+
self.dtype = "float32"
26+
27+
def compute(self):
28+
assert len(self.length) == self.x_shape[0]
29+
x = np.random.random(self.x_shape).astype(self.dtype)
30+
out_lod = [self.length]
31+
32+
out = x[0, 0:self.length[0]]
33+
for i in six.moves.xrange(1, x.shape[0]):
34+
out = np.append(out, x[i, 0:self.length[i]], axis=0)
35+
36+
out_shape = (sum(self.length), )
37+
if len(self.x_shape) == 2:
38+
out_shape = out_shape + (1, )
39+
else:
40+
out_shape = out_shape + self.x_shape[2:]
41+
42+
self.inputs = {
43+
'X': x,
44+
'Length': np.array(self.length).astype('int64').reshape(-1, 1)
45+
}
46+
self.outputs = {'Out': (out.reshape(out_shape), out_lod)}
47+
48+
def setUp(self):
49+
self.op_type = 'sequence_unpad'
50+
self.init()
51+
self.compute()
52+
53+
def test_check_output(self):
54+
self.check_output()
55+
56+
def test_check_grad(self):
57+
self.check_grad(["X"], "Out")
58+
59+
60+
class TestSequenceUnpadOp2(TestSequenceUnpadOp):
61+
def init(self):
62+
self.length = [2, 3, 4]
63+
self.x_shape = (3, 5, 4, 3)
64+
self.dtype = "float32"
65+
66+
67+
class TestSequenceUnpadOp3(TestSequenceUnpadOp):
68+
def init(self):
69+
self.length = [5, 2, 3, 4]
70+
self.x_shape = (4, 5, 3, 3, 6)
71+
self.dtype = "float64"
72+
73+
74+
if __name__ == '__main__':
75+
unittest.main()

0 commit comments

Comments
 (0)