Skip to content

Commit 46b0b79

Browse files
author
Yibing Liu
authored
Merge pull request #13856 from kuke/seq_unpad_op
Add sequence unpad op
2 parents dcfb687 + 699825a commit 46b0b79

File tree

7 files changed

+437
-3
lines changed

7 files changed

+437
-3
lines changed

paddle/fluid/API.spec

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ paddle.fluid.layers.conv2d_transpose ArgSpec(args=['input', 'num_filters', 'outp
7575
paddle.fluid.layers.conv3d_transpose ArgSpec(args=['input', 'num_filters', 'output_size', 'filter_size', 'padding', 'stride', 'dilation', 'groups', 'param_attr', 'bias_attr', 'use_cudnn', 'act', 'name'], varargs=None, keywords=None, defaults=(None, None, 0, 1, 1, None, None, None, True, None, None))
7676
paddle.fluid.layers.sequence_expand ArgSpec(args=['x', 'y', 'ref_level', 'name'], varargs=None, keywords=None, defaults=(-1, None))
7777
paddle.fluid.layers.sequence_expand_as ArgSpec(args=['x', 'y', 'name'], varargs=None, keywords=None, defaults=(None,))
78-
paddle.fluid.layers.sequence_pad ArgSpec(args=['x', 'pad_value', 'maxlen'], varargs=None, keywords=None, defaults=(None,))
78+
paddle.fluid.layers.sequence_pad ArgSpec(args=['x', 'pad_value', 'maxlen', 'name'], varargs=None, keywords=None, defaults=(None, None))
79+
paddle.fluid.layers.sequence_unpad ArgSpec(args=['x', 'length', 'name'], varargs=None, keywords=None, defaults=(None,))
7980
paddle.fluid.layers.lstm_unit ArgSpec(args=['x_t', 'hidden_t_prev', 'cell_t_prev', 'forget_bias', 'param_attr', 'bias_attr', 'name'], varargs=None, keywords=None, defaults=(0.0, None, None, None))
8081
paddle.fluid.layers.reduce_sum ArgSpec(args=['input', 'dim', 'keep_dim', 'name'], varargs=None, keywords=None, defaults=(None, False, None))
8182
paddle.fluid.layers.reduce_mean ArgSpec(args=['input', 'dim', 'keep_dim', 'name'], varargs=None, keywords=None, defaults=(None, False, None))
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

python/paddle/fluid/layers/nn.py

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
'sequence_expand',
5757
'sequence_expand_as',
5858
'sequence_pad',
59+
'sequence_unpad',
5960
'lstm_unit',
6061
'reduce_sum',
6162
'reduce_mean',
@@ -2793,7 +2794,7 @@ def sequence_expand_as(x, y, name=None):
27932794

27942795

27952796
@templatedoc()
2796-
def sequence_pad(x, pad_value, maxlen=None):
2797+
def sequence_pad(x, pad_value, maxlen=None, name=None):
27972798
"""
27982799
${comment}
27992800
@@ -2807,7 +2808,9 @@ def sequence_pad(x, pad_value, maxlen=None):
28072808
None or any positive int. When it is None, all sequences will be
28082809
padded up to the length of the longest one among them; when it a
28092810
certain positive value, it must be greater than the length of the
2810-
longest original sequence."
2811+
longest original sequence.
2812+
name(str|None): A name for this layer(optional). If set None, the layer
2813+
will be named automatically.
28112814
28122815
Returns:
28132816
Variable: The padded sequence batch and the original lengths before
@@ -2844,6 +2847,66 @@ def sequence_pad(x, pad_value, maxlen=None):
28442847
return out, length
28452848

28462849

2850+
def sequence_unpad(x, length, name=None):
2851+
"""
2852+
**Sequence Unpad Layer**
2853+
2854+
This layer removes the padding data in the input sequences and convert
2855+
them into sequences with actual length as output, identitied by lod
2856+
information.
2857+
2858+
.. code-block:: text
2859+
2860+
Example:
2861+
2862+
Given input Variable **x**:
2863+
x.data = [[ 1.0, 2.0, 3.0, 4.0, 5.0],
2864+
[ 6.0, 7.0, 8.0, 9.0, 10.0],
2865+
[11.0, 12.0, 13.0, 14.0, 15.0]],
2866+
2867+
in which there are 3 sequences padded to length 5, and the acutal length
2868+
specified by input Variable **length**:
2869+
2870+
length.data = [[2], [3], [4]],
2871+
2872+
after unpadding, the output Variable will be:
2873+
2874+
out.data = [[1.0, 2.0, 6.0, 7.0, 8.0, 11.0, 12.0, 13.0, 14.0]]
2875+
out.lod = [[2, 3, 4]]
2876+
2877+
Args:
2878+
x(Variable): Input Variable which contains the padded sequences with
2879+
equal length.
2880+
length(Variable): The Variable that specifies the actual ength of
2881+
sequences after unpadding.
2882+
name(str|None): A name for this layer(optional). If set None, the layer
2883+
will be named automatically.
2884+
2885+
Returns:
2886+
Variable: The Variable contains the unpadded sequences.
2887+
2888+
Examples:
2889+
.. code-block:: python
2890+
2891+
x = fluid.layers.data(name='x', shape=[10, 5], dtype='float32')
2892+
len = fluid.layers.data(name='length', shape=[1], dtype='int64')
2893+
out = fluid.layers.sequence_unpad(x=x, length=len)
2894+
"""
2895+
2896+
helper = LayerHelper('sequence_unpad', input=x, **locals())
2897+
dtype = helper.input_dtype()
2898+
out = helper.create_tmp_variable(dtype)
2899+
2900+
length.stop_gradient = True
2901+
2902+
helper.append_op(
2903+
type='sequence_unpad',
2904+
inputs={'X': x,
2905+
'Length': length},
2906+
outputs={'Out': out})
2907+
return out
2908+
2909+
28472910
def beam_search(pre_ids,
28482911
pre_scores,
28492912
ids,

0 commit comments

Comments
 (0)