Skip to content

Commit 58730ba

Browse files
committed
Enhance unit test.
1 parent bf3f56e commit 58730ba

File tree

5 files changed

+145
-101
lines changed

5 files changed

+145
-101
lines changed

paddle/fluid/operators/sequence_expand_op.cc

Lines changed: 54 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,11 @@ class SequenceExpandOp : public framework::OperatorWithKernel {
3333
"Output(Out) of SequenceExpandOp should not be null.");
3434

3535
auto x_dims = ctx->GetInputDim("X");
36+
auto out_dims = x_dims;
3637
int ref_level = ctx->Attrs().Get<int>("ref_level");
3738

38-
PADDLE_ENFORCE_EQ(x_dims.size(), 2U,
39-
"Dimension number of Input(X) should be 2.");
39+
PADDLE_ENFORCE_GE(x_dims.size(), 2,
40+
"Dimension number of Input(X) should be at least 2.");
4041

4142
if (ctx->IsRuntime()) {
4243
framework::Variable* x_var =
@@ -50,15 +51,9 @@ class SequenceExpandOp : public framework::OperatorWithKernel {
5051
PADDLE_ENFORCE_LE(x_lod.size(), 1,
5152
"Number of lod level of Input(X) should not be "
5253
"greater than 1.");
53-
54-
PADDLE_ENFORCE(x_lod.size() == y_lod.size() || x_lod.size() == 0,
55-
"Level number of Input(X)'s lod should be either equal "
56-
"to 0 or equal to that of Input(Y).");
57-
5854
PADDLE_ENFORCE_GT(y_lod.size(), 0,
5955
"Level number of Input(Y)'s lod should be "
6056
"greater than 0.");
61-
6257
PADDLE_ENFORCE(
6358
ref_level == -1 ||
6459
(ref_level >= 0 && ref_level < static_cast<int>(y_lod.size())),
@@ -68,6 +63,14 @@ class SequenceExpandOp : public framework::OperatorWithKernel {
6863

6964
if (ref_level == -1) ref_level = y_lod.size() - 1;
7065

66+
if (x_lod.size() > 0) {
67+
PADDLE_ENFORCE(
68+
x_lod.size() == 0 || x_lod[0].size() == y_lod[ref_level].size(),
69+
"Level number of Input(X)'s lod should be 0. Otherwise "
70+
"size of Input(X)'s first level lod should be equal to "
71+
"size of Input(Y)'s lod of referred level.");
72+
}
73+
7174
int64_t out_first_dim = 0;
7275
if (y_lod[ref_level].size() <= 1) {
7376
out_first_dim = x_dims[0];
@@ -81,9 +84,12 @@ class SequenceExpandOp : public framework::OperatorWithKernel {
8184
(y_lod[ref_level][i] - y_lod[ref_level][i - 1]) * x_seq_len;
8285
}
8386
}
84-
ctx->SetOutputDim("Out", {out_first_dim, x_dims[1]});
87+
out_dims[0] = out_first_dim;
88+
ctx->SetOutputDim("Out", out_dims);
8589
} else {
86-
ctx->SetOutputDim("Out", {-1, x_dims[1]});
90+
out_dims[0] = -1;
91+
ctx->SetOutputDim("Out", out_dims);
92+
ctx->ShareLoD("X", /*->*/ "Out");
8793
}
8894
}
8995
};
@@ -105,69 +111,69 @@ class SequenceExpandOpMaker : public framework::OpProtoAndCheckerMaker {
105111
AddComment(R"DOC(
106112
Sequence Expand Operator.
107113
108-
This operator expands input(X) according to LOD of input(Y).
114+
This operator expands `X` according to specified level lod of `Y`. Current
115+
implementation constaints that lod level of `X` should be at most 1. Attribute
116+
`ref_level` is used to specify which level lod of `Y` is referred to expand `X`.
117+
If set `ref_level` to -1, then last level lod of `Y` would be referred.
118+
Please note, rank of `X` should be at least 2, when the rank exceeds 2, `X`
119+
would be viewed as a 2-D tensor.
120+
109121
Following are cases to better explain how this works:
122+
110123
Case 1:
111124
112-
Given a 2-level LoDTensor input(X)
113-
X.lod = [[0, 2, 3],
114-
[0, 1, 3, 4]]
115-
X.data = [a, b, c, d]
125+
Given a 1-level LoDTensor input(X)
126+
X.lod = [[0, 2, 4]]
127+
X.data = [[a], [b], [c], [d]]
116128
X.dims = [4, 1]
117129
and input(Y)
118130
Y.lod = [[0, 2, 4],
119131
[0, 3, 6, 7, 8]]
120-
with condition len(Y.lod[-1]) -1 == X.dims[0]
121-
then we get 2-level LoDTensor
122-
Out.lod = [[0, 2, 4],
123-
[0, 3, 6, 7, 8]]
124-
Out.data = [a, a, a, b, b, b, c, d]
132+
ref_level: 0
133+
then we get 1-level LoDTensor
134+
Out.lod = [[0, 2, 4, 6, 8]]
135+
Out.data = [[a], [b], [a], [b], [c], [d], [c], [d]]
125136
Out.dims = [8, 1]
126137
127138
Case 2:
128139
140+
Given 1-level LoDTensor input(X)
141+
X.lod = [[0, 1, 4]]
142+
X.data = [[a], [b], [c], [d]]
143+
X.dims = [4, 1]
144+
and input(Y)
145+
Y.lod = [[0, 2, 4],
146+
[0, 3, 6, 6, 8]]
147+
ref_level: 0
148+
then we get 1-level LoDTensor
149+
Out.lod = [[0, 2, 5, 8]]
150+
Out.data = [[a], [a], [b], [c], [d], [b], [c], [d]]
151+
Out.dims = [8, 1]
152+
153+
Case 3:
154+
129155
Given a common Tensor input(X)
130-
X.data = [a, b, c]
156+
X.data = [[a], [b], [c]]
131157
X.dims = [3, 1]
132158
and input(Y)
133159
Y.lod = [[0, 2, 3, 6]]
134-
with condition len(Y.lod[-1]) -1 == X.dims[0]
135-
then we get 1-level LoDTensor
136-
Out.lod = [[0, 2, 3, 6]]
137-
Out.data = [a, a, b, c, c, c]
160+
ref_level: -1
161+
then we a common Tensor
162+
Out.data = [[a], [a], [b], [c], [c], [c]]
138163
Out.dims = [6, 1]
139164
140-
Case 3:
165+
Case 4:
141166
142167
Given a common Tensor input(X)
143168
X.data = [[a, b], [c, d], [e, f]]
144169
X.dims = [3, 2]
145170
and input(Y)
146171
Y.lod = [[0, 2, 3, 6]]
147-
with condition len(Y.lod[-1]) -1 == X.dims[0]
148-
then we get 1-level LoDTensor
149-
Out.lod = [[0, 2, 3, 6]]
150-
Out.data = [[a,b], [a,b] [c,d], [e, f], [e, f], [e, f]]
172+
ref_level: 0
173+
then we get a common LoDTensor
174+
Out.data = [[a, b], [a, b] [c, d], [e, f], [e, f], [e, f]]
151175
Out.dims = [6, 2]
152176
153-
Case 4:
154-
155-
Given 2-level a LoDTensor input(X)
156-
X.lod = [[0, 2, 3],
157-
[0, 1, 3, 4]]
158-
X.data = [a, b, c, d]
159-
X.dims = [4, 1]
160-
and input(Y)
161-
Y.lod = [[0, 2, 4],
162-
[0, 3, 6, 6, 8]]
163-
with condition len(Y.lod[-1]) -1 == X.dims[0]
164-
then we get 2-level LoDTensor
165-
Out.lod = [[0, 2, 4],
166-
[0, 3, 6, 6, 8]]
167-
Out.data = [a, a, a, b, b, b, d, d]
168-
Out.dims = [8, 1]
169-
170-
171177
)DOC");
172178
}
173179
};

paddle/fluid/operators/sequence_expand_op.h

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ namespace paddle {
2222
namespace operators {
2323

2424
using LoDTensor = framework::LoDTensor;
25+
template <typename T, int MajorType = Eigen::RowMajor,
26+
typename IndexType = Eigen::DenseIndex>
27+
using EigenMatrix = framework::EigenMatrix<T, MajorType, IndexType>;
2528

2629
template <typename DeviceContext, typename T>
2730
class SequenceExpandKernel : public framework::OpKernel<T> {
@@ -30,15 +33,12 @@ class SequenceExpandKernel : public framework::OpKernel<T> {
3033
auto* x = context.Input<LoDTensor>("X");
3134
auto* y = context.Input<LoDTensor>("Y");
3235
auto* out = context.Output<LoDTensor>("Out");
33-
int ref_level = context.Attr<int>("ref_level");
3436

35-
out->mutable_data<T>(context.GetPlace());
37+
int ref_level = context.Attr<int>("ref_level");
3638
auto& x_lod = x->lod();
3739
auto& y_lod = y->lod();
38-
3940
PADDLE_ENFORCE_GT(y_lod.size(), 0,
4041
"Level number of `Y`'s lod should be greater than 0.");
41-
4242
PADDLE_ENFORCE(
4343
ref_level == -1 || (ref_level >= 0 && ref_level < y_lod.size()),
4444
"Invlid `ref_level`, which should be either equal to -1 "
@@ -47,6 +47,8 @@ class SequenceExpandKernel : public framework::OpKernel<T> {
4747

4848
if (ref_level == -1) ref_level = y_lod.size() - 1;
4949

50+
out->mutable_data<T>(context.GetPlace());
51+
5052
if (y_lod[ref_level].size() <= 1) {
5153
framework::TensorCopy(*x, context.GetPlace(), out);
5254
return;
@@ -59,6 +61,8 @@ class SequenceExpandKernel : public framework::OpKernel<T> {
5961
}
6062

6163
int out_offset = 0;
64+
auto& eigen_place =
65+
*context.template device_context<DeviceContext>().eigen_device();
6266
for (size_t i = 1; i < y_lod[ref_level].size(); ++i) {
6367
int repeat_num = y_lod[ref_level][i] - y_lod[ref_level][i - 1];
6468
int x_start = i - 1;
@@ -68,16 +72,24 @@ class SequenceExpandKernel : public framework::OpKernel<T> {
6872
x_end = x_lod[0][i];
6973
}
7074
int x_seq_len = x_end - x_start;
71-
auto x_sub_tensor = x->Slice(x_start, x_end);
72-
for (size_t j = 0; j < repeat_num; ++j) {
75+
if (repeat_num > 0) {
76+
auto x_sub_tensor = x->Slice(x_start, x_end);
77+
x_sub_tensor.Resize({1, x_sub_tensor.numel()});
7378
int out_start = out_offset;
7479
if (x_lod.size() == 1) {
7580
out_start = out_lod[0][out_offset];
76-
out_lod[0].push_back(x_seq_len);
7781
}
78-
auto out_sub_tensor = out->Slice(out_start, out_start + x_seq_len);
79-
framework::TensorCopy(x_sub_tensor, context.GetPlace(),
80-
&out_sub_tensor);
82+
auto out_sub_tensor =
83+
out->Slice(out_start, out_start + x_seq_len * repeat_num);
84+
out_sub_tensor.Resize({repeat_num, x_sub_tensor.dims()[1]});
85+
EigenMatrix<T>::From(out_sub_tensor).device(eigen_place) =
86+
EigenMatrix<T>::From(x_sub_tensor)
87+
.broadcast(Eigen::array<int, 2>({{repeat_num, 1}}));
88+
}
89+
for (int j = 0; j < repeat_num; ++j) {
90+
if (x_lod.size() == 1) {
91+
out_lod[0].push_back(out_lod[0].back() + x_seq_len);
92+
}
8193
out_offset++;
8294
}
8395
}
@@ -122,6 +134,9 @@ class SequenceExpandGradKernel : public framework::OpKernel<T> {
122134

123135
auto& dev_ctx = context.template device_context<DeviceContext>();
124136

137+
math::SetConstant<DeviceContext, T> set_zero;
138+
set_zero(dev_ctx, g_x, static_cast<T>(0));
139+
125140
int g_out_offset = 0;
126141
for (size_t i = 1; i < y_lod[ref_level].size(); ++i) {
127142
int repeat_num = y_lod[ref_level][i] - y_lod[ref_level][i - 1];
@@ -133,12 +148,11 @@ class SequenceExpandGradKernel : public framework::OpKernel<T> {
133148
x_end = x_lod[0][i];
134149
}
135150
int x_seq_len = x_end - x_start;
136-
auto column = x_seq_len * x->dims()[1];
137151
auto g_x_sub = g_x->Slice(x_start, x_end);
138-
g_x_sub = framework::ReshapeToMatrix(g_x_sub, column);
152+
g_x_sub.Resize(flatten_to_1d(g_x_sub.dims()));
139153
int g_out_end = g_out_offset + repeat_num * x_seq_len;
140154
auto g_out_sub = g_out->Slice(g_out_offset, g_out_end);
141-
g_out_sub = framework::ReshapeToMatrix(g_out_sub, column);
155+
g_out_sub.Resize({repeat_num, g_x_sub.dims()[0]});
142156
math::ColwiseSum<DeviceContext, T> col_sum;
143157
col_sum(dev_ctx, g_out_sub, &g_x_sub);
144158
g_out_offset += repeat_num * x_seq_len;

python/paddle/fluid/layers/nn.py

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1781,52 +1781,52 @@ def conv2d_transpose(input,
17811781
return out
17821782

17831783

1784-
def sequence_expand(x, y, name=None):
1784+
def sequence_expand(x, y, ref_level=-1, name=None):
17851785
"""Sequence Expand Layer. This layer will expand the input variable **x**
1786-
according to LoD information of **y**. And the following examples will
1787-
explain how sequence_expand works:
1786+
according to specified level lod of **y**. Please note that lod level of
1787+
**x** is at most 1 and rank of **x** is at least 2. When rank of **x**
1788+
is greater than 2, then it would be viewed as a 2-D tensor.
1789+
Following examples will explain how sequence_expand works:
17881790
17891791
.. code-block:: text
17901792
17911793
* Case 1
17921794
x is a LoDTensor:
1793-
x.lod = [[0, 2, 3],
1794-
[0, 1, 3, 4]]
1795-
x.data = [a, b, c, d]
1795+
x.lod = [[0, 2, 4]]
1796+
x.data = [[a], [b], [c], [d]]
17961797
x.dims = [4, 1]
17971798
17981799
y is a LoDTensor:
17991800
y.lod = [[0, 2, 4],
18001801
[0, 3, 6, 7, 8]]
18011802
1802-
with condition len(y.lod[-1]) - 1 == x.dims[0]
1803+
ref_level: 0
18031804
1804-
then output is a 2-level LoDTensor:
1805-
out.lod = [[0, 2, 4],
1806-
[0, 3, 6, 7, 8]]
1807-
out.data = [a, a, a, b, b, b, c, d]
1805+
then output is a 1-level LoDTensor:
1806+
out.lod = [[0, 2, 4, 6, 8]]
1807+
out.data = [[a], [b], [a], [b], [c], [d], [c], [d]]
18081808
out.dims = [8, 1]
18091809
18101810
* Case 2
18111811
x is a Tensor:
1812-
x.data = [a, b, c]
1812+
x.data = [[a], [b], [c]]
18131813
x.dims = [3, 1]
18141814
18151815
y is a LoDTensor:
1816-
y.lod = [[0, 2, 3, 6]]
1817-
1818-
with condition len(y.lod[-1]) - 1 == x.dims[0]
1816+
y.lod = [[0, 2, 2, 5]]
18191817
1820-
then output is a 1-level LoDTensor:
1821-
out.lod = [[0, 2, 3, 6]]
1822-
out.data = [a, a, b, c, c, c]
1823-
out.dims = [6, 1]
1818+
ref_level: -1
18241819
1820+
then output is a Tensor:
1821+
out.data = [[a], [a], [c], [c], [c]]
1822+
out.dims = [5, 1]
18251823
Args:
18261824
x (Variable): The input variable which is a Tensor or LoDTensor.
18271825
y (Variable): The input variable which is a LoDTensor.
1826+
ref_level (int): Lod level of `y` to be referred by `x`. If set to -1,
1827+
refer the last level of lod.
18281828
name(str|None): A name for this layer(optional). If set None, the layer
1829-
will be named automatically.
1829+
will be named automatically.
18301830
18311831
Returns:
18321832
Variable: The expanded variable which is a LoDTensor.
@@ -1837,14 +1837,17 @@ def sequence_expand(x, y, name=None):
18371837
x = fluid.layers.data(name='x', shape=[10], dtype='float32')
18381838
y = fluid.layers.data(name='y', shape=[10, 20],
18391839
dtype='float32', lod_level=1)
1840-
out = layers.sequence_expand(x=x, y=y)
1840+
out = layers.sequence_expand(x=x, y=y, ref_level=0)
18411841
"""
18421842
helper = LayerHelper('sequence_expand', input=x, **locals())
18431843
dtype = helper.input_dtype()
18441844
tmp = helper.create_tmp_variable(dtype)
18451845
helper.append_op(
1846-
type='sequence_expand', inputs={'X': x,
1847-
'Y': y}, outputs={'Out': tmp})
1846+
type='sequence_expand',
1847+
inputs={'X': x,
1848+
'Y': y},
1849+
outputs={'Out': tmp},
1850+
attrs={'ref_level': ref_level})
18481851
return tmp
18491852

18501853

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,8 @@ def test_sequence_expand(self):
181181
with program_guard(program):
182182
x = layers.data(name='x', shape=[10], dtype='float32')
183183
y = layers.data(
184-
name='y', shape=[10, 20], dtype='float32', lod_level=1)
185-
self.assertIsNotNone(layers.sequence_expand(x=x, y=y))
184+
name='y', shape=[10, 20], dtype='float32', lod_level=2)
185+
self.assertIsNotNone(layers.sequence_expand(x=x, y=y, ref_level=1))
186186
print(str(program))
187187

188188
def test_lstm_unit(self):

0 commit comments

Comments
 (0)