Skip to content

Commit e895c98

Browse files
committed
add support to max_len is None
1 parent 64464cb commit e895c98

File tree

4 files changed

+112
-34
lines changed

4 files changed

+112
-34
lines changed

paddle/fluid/API.spec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ paddle.fluid.layers.crop ArgSpec(args=['x', 'shape', 'offsets', 'name'], varargs
162162
paddle.fluid.layers.rank_loss ArgSpec(args=['label', 'left', 'right', 'name'], varargs=None, keywords=None, defaults=(None,))
163163
paddle.fluid.layers.prelu ArgSpec(args=['x', 'mode', 'param_attr', 'name'], varargs=None, keywords=None, defaults=(None, None))
164164
paddle.fluid.layers.flatten ArgSpec(args=['x', 'axis', 'name'], varargs=None, keywords=None, defaults=(1, None))
165-
paddle.fluid.layers.sequence_mask ArgSpec(args=['x', 'max_len', 'mask_dtype'], varargs=None, keywords=None, defaults=('int64',))
165+
paddle.fluid.layers.sequence_mask ArgSpec(args=['x', 'maxlen', 'dtype', 'name'], varargs=None, keywords=None, defaults=(None, 'int64', None))
166166
paddle.fluid.layers.data ArgSpec(args=['name', 'shape', 'append_batch_size', 'dtype', 'lod_level', 'type', 'stop_gradient'], varargs=None, keywords=None, defaults=(True, 'float32', 0, VarType.LOD_TENSOR, True))
167167
paddle.fluid.layers.open_recordio_file ArgSpec(args=['filename', 'shapes', 'lod_levels', 'dtypes', 'pass_num', 'for_parallel'], varargs=None, keywords=None, defaults=(1, True))
168168
paddle.fluid.layers.open_files ArgSpec(args=['filenames', 'shapes', 'lod_levels', 'dtypes', 'thread_num', 'buffer_size', 'pass_num', 'is_test'], varargs=None, keywords=None, defaults=(None, None, 1, None))

paddle/fluid/operators/sequence_mask_op.h

Lines changed: 60 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@
1414

1515
#pragma once
1616

17+
#ifdef __NVCC__
18+
#include <thrust/device_ptr.h>
19+
#include <thrust/functional.h>
20+
#include <thrust/reduce.h>
21+
#else
22+
#include <algorithm>
23+
#endif
24+
1725
#include "paddle/fluid/framework/op_registry.h"
1826
#include "paddle/fluid/platform/for_range.h"
1927

@@ -26,73 +34,83 @@ class SequenceMaskOp : public framework::OperatorWithKernel {
2634

2735
void InferShape(framework::InferShapeContext *ctx) const override {
2836
PADDLE_ENFORCE(ctx->HasInput("X"), "Input(X) must exist");
29-
auto max_len = ctx->Attrs().Get<int>("max_len");
30-
PADDLE_ENFORCE_GT(max_len, 1, "Attr(max_len) must be larger than 1");
3137
PADDLE_ENFORCE(ctx->HasOutput("Y"), "Output(Y) must exist");
32-
auto dim = framework::vectorize2int(ctx->GetInputDim("X"));
33-
dim.push_back(max_len);
34-
ctx->SetOutputDim("Y", framework::make_ddim(dim));
38+
39+
auto maxlen = ctx->Attrs().Get<int>("maxlen");
40+
if (maxlen > 0) { // We can only infershape when maxlen > 0
41+
auto dim = framework::vectorize2int(ctx->GetInputDim("X"));
42+
dim.push_back(maxlen);
43+
ctx->SetOutputDim("Y", framework::make_ddim(dim));
44+
}
3545
}
3646
};
3747

3848
class SequenceMaskOpMaker : public framework::OpProtoAndCheckerMaker {
3949
public:
4050
void Make() override {
41-
AddInput("X", "The input of sequence_mask op.");
51+
AddInput("X", "The input tensor of sequence_mask op.");
4252
AddOutput("Y", "The output mask of sequence_mask op.");
43-
AddAttr<int>("max_len", "The maximum length of the sequence.")
44-
.GreaterThan(1);
53+
AddAttr<int>("maxlen",
54+
"The maximum length of the sequence. If maxlen < 0, maxlen "
55+
"= max(Input(X)).")
56+
.SetDefault(-1)
57+
.AddCustomChecker([](int &v) {
58+
PADDLE_ENFORCE(v < 0 || v >= 1,
59+
"Attr(maxlen) must be less than 0 or larger than 1");
60+
});
4561
AddAttr<int>("out_dtype", "Output data type");
4662
AddComment(R"DOC(
4763
SequenceMask Operator
4864
49-
This operator outputs a Mask according to Input(X) and Attr(max_len).
65+
This operator outputs a Mask according to Input(X) and Attr(maxlen).
5066
Supposing Input(X) is a Tensor with shape [d_1, d_2, ..., d_n], the
51-
Output(Y) is a mask with shape [d_1, d_2, ..., d_n, max_len], where:
67+
Output(Y) is a mask with shape [d_1, d_2, ..., d_n, maxlen], where:
5268
5369
Y(i_1, i_2, ..., i_n, j) = (j < X(i_1, i_2, ..., i_n))
70+
71+
If maxlen < 0, maxlen = max(X)
5472
)DOC");
5573
}
5674
};
5775

5876
template <typename Tx, typename Ty>
5977
struct SequenceMaskForRangeFunctor {
60-
HOSTDEVICE SequenceMaskForRangeFunctor(const Tx *x, Ty *y, int max_len)
61-
: x_(x), y_(y), max_len_(max_len) {}
78+
HOSTDEVICE SequenceMaskForRangeFunctor(const Tx *x, Ty *y, int maxlen)
79+
: x_(x), y_(y), maxlen_(maxlen) {}
6280

6381
HOSTDEVICE void operator()(int y_idx) const {
64-
int x_idx = y_idx / max_len_;
65-
int j = y_idx % max_len_;
82+
int x_idx = y_idx / maxlen_;
83+
int j = y_idx % maxlen_;
6684
y_[y_idx] = static_cast<Ty>(j < x_[x_idx] ? 1 : 0);
6785
}
6886

6987
private:
7088
const Tx *x_;
7189
Ty *y_;
72-
int max_len_;
90+
int maxlen_;
7391
};
7492

7593
template <typename DeviceContext, typename Tx>
7694
struct SequenceMaskFunctor {
7795
using Tensor = framework::LoDTensor;
7896

7997
SequenceMaskFunctor(const DeviceContext &ctx, const Tx *x, Tensor *y,
80-
int limits, int max_len)
81-
: ctx_(ctx), x_(x), y_(y), limits_(limits), max_len_(max_len) {}
98+
int limits, int maxlen)
99+
: ctx_(ctx), x_(x), y_(y), limits_(limits), maxlen_(maxlen) {}
82100

83101
template <typename Ty>
84102
void operator()() const {
85103
auto *y_data = y_->mutable_data<Ty>(ctx_.GetPlace());
86104
platform::ForRange<DeviceContext> for_range(ctx_, limits_);
87-
for_range(SequenceMaskForRangeFunctor<Tx, Ty>(x_, y_data, max_len_));
105+
for_range(SequenceMaskForRangeFunctor<Tx, Ty>(x_, y_data, maxlen_));
88106
}
89107

90108
private:
91109
const DeviceContext &ctx_;
92110
const Tx *x_;
93111
Tensor *y_;
94112
int limits_;
95-
int max_len_;
113+
int maxlen_;
96114
};
97115

98116
template <typename DeviceContext, typename Tx>
@@ -103,13 +121,32 @@ class SequenceMaskKernel : public framework::OpKernel<Tx> {
103121
void Compute(const framework::ExecutionContext &ctx) const override {
104122
auto *x = ctx.Input<Tensor>("X");
105123
auto *y = ctx.Output<Tensor>("Y");
106-
auto max_len = ctx.Attr<int>("max_len");
124+
auto maxlen = ctx.Attr<int>("maxlen");
125+
126+
auto *x_data = x->data<Tx>();
127+
auto x_numel = x->numel();
128+
if (maxlen < 0) {
129+
#ifdef __NVCC__
130+
VLOG(10)
131+
<< "SequenceMaskOp on GPU may be slow when maxlen is not provided.";
132+
maxlen = static_cast<int>(
133+
thrust::reduce(thrust::device_pointer_cast(x_data),
134+
thrust::device_pointer_cast(x_data) + x_numel,
135+
static_cast<Tx>(0), thrust::maximum<Tx>()));
136+
#else
137+
maxlen = static_cast<int>(*std::max_element(x_data, x_data + x_numel));
138+
#endif
139+
auto y_dim = framework::vectorize2int(x->dims());
140+
y_dim.push_back(maxlen);
141+
y->Resize(framework::make_ddim(y_dim));
142+
}
143+
107144
auto out_dtype = static_cast<framework::proto::VarType::Type>(
108145
ctx.Attr<int>("out_dtype"));
109146
auto &dev_ctx = ctx.template device_context<DeviceContext>();
110-
framework::VisitDataType(out_dtype, SequenceMaskFunctor<DeviceContext, Tx>(
111-
dev_ctx, x->data<Tx>(), y,
112-
x->numel() * max_len, max_len));
147+
framework::VisitDataType(out_dtype,
148+
SequenceMaskFunctor<DeviceContext, Tx>(
149+
dev_ctx, x_data, y, x_numel * maxlen, maxlen));
113150
}
114151
};
115152

python/paddle/fluid/layers/nn.py

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5525,13 +5525,46 @@ def flatten(x, axis=1, name=None):
55255525
return out
55265526

55275527

5528-
def sequence_mask(x, max_len, mask_dtype='int64'):
5528+
def sequence_mask(x, maxlen=None, dtype='int64', name=None):
5529+
"""
5530+
**SequenceMask Layer**
5531+
5532+
This layer outputs a mask according to the input :code:`x` and
5533+
:code:`maxlen` with data type of :code:`dtype`.
5534+
5535+
Supposing :code:`x` is a Tensor with shape [d_1, d_2, ..., d_n], the
5536+
:code:`y` is a mask with shape [d_1, d_2, ..., d_n, maxlen], where:
5537+
5538+
.. math::
5539+
5540+
y(i_1, i_2,..., i_n, j) = (j < x(i_1, i_2,..., i_n))
5541+
5542+
Args:
5543+
x (Variable): Input tensor of sequence_mask layer,
5544+
whose elements are integers less than :code:`maxlen`.
5545+
maxlen (int|None): Maximum length of the sequence. If :code:`maxlen`
5546+
is None, it would be replace with :math:`max(x)`.
5547+
dtype (np.dtype|core.VarDesc.VarType|str): Data type of the output.
5548+
name (str|None): A name for this layer(optional). If set None, the
5549+
layer will be named automatically.
5550+
5551+
Returns:
5552+
Variable: The output sequence mask.
5553+
5554+
"""
5555+
55295556
helper = LayerHelper('sequence_mask', **locals())
5530-
y = helper.create_tmp_variable(dtype=mask_dtype)
5557+
if name is None:
5558+
out = helper.create_tmp_variable(dtype=dtype)
5559+
else:
5560+
out = helper.create_tmp_variable(dtype=dtype, name=name)
5561+
55315562
helper.append_op(
55325563
type='sequence_mask',
55335564
inputs={'X': [x]},
5534-
outputs={'Y': y},
5535-
attrs={'max_len': max_len,
5536-
'out_dtype': y.dtype})
5537-
return y
5565+
outputs={'Y': out},
5566+
attrs={
5567+
'max_len': maxlen if maxlen is not None else -1,
5568+
'out_dtype': out.dtype
5569+
})
5570+
return out

python/paddle/fluid/tests/unittests/test_sequence_mask.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
# limitations under the License.
1414

1515
from op_test import OpTest
16+
import paddle.fluid as fluid
1617
from paddle.fluid.framework import convert_np_dtype_to_dtype_
18+
import paddle.fluid.core as core
1719
import numpy as np
1820
import copy
1921
import unittest
@@ -22,7 +24,7 @@
2224
class SequenceMaskTestBase(OpTest):
2325
def initDefaultParameters(self):
2426
self.op_type = 'sequence_mask'
25-
self.max_len = 10
27+
self.maxlen = 10
2628
self.mask_dtype = 'int64'
2729
self.x = [[0, 3, 4], [5, 7, 9]]
2830

@@ -38,15 +40,16 @@ def setUp(self):
3840
self.inputs = {'X': self.x}
3941
self.outputs = {'Y': self.calc_ground_truth_mask()}
4042
self.attrs = {
41-
'max_len': self.max_len,
43+
'maxlen': self.maxlen,
4244
'out_dtype': convert_np_dtype_to_dtype_(self.mask_dtype)
4345
}
4446

4547
def calc_ground_truth_mask(self):
46-
shape = self.x.shape + (self.max_len, )
48+
maxlen = np.max(self.x) if self.maxlen < 0 else self.maxlen
49+
shape = self.x.shape + (maxlen, )
4750
index_broadcast = np.broadcast_to(
4851
np.reshape(
49-
range(self.max_len), newshape=[1] * self.x.ndim + [-1]),
52+
range(maxlen), newshape=[1] * self.x.ndim + [-1]),
5053
shape=shape)
5154
x_broadcast = np.broadcast_to(
5255
np.reshape(
@@ -82,5 +85,10 @@ def initParameters(self):
8285
self.mask_dtype = 'float64'
8386

8487

88+
class SequenceMaskTest6(SequenceMaskTestBase):
89+
def initParameters(self):
90+
self.maxlen = -1
91+
92+
8593
if __name__ == '__main__':
8694
unittest.main()

0 commit comments

Comments
 (0)