parent
e593113a84
commit
1e60c9b2e8
@ -0,0 +1,166 @@
|
|||||||
|
/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License. */
|
||||||
|
|
||||||
|
#include "paddle/operators/sequence_project_op.h"
|
||||||
|
|
||||||
|
namespace paddle {
|
||||||
|
namespace operators {
|
||||||
|
|
||||||
|
class SequenceProjectOp : public framework::OperatorWithKernel {
|
||||||
|
public:
|
||||||
|
using framework::OperatorWithKernel::OperatorWithKernel;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void InferShape(framework::InferShapeContext* ctx) const override {
|
||||||
|
PADDLE_ENFORCE(ctx->HasInput("X"),
|
||||||
|
"Input(X) of SequenceProjectOp should not be null.");
|
||||||
|
PADDLE_ENFORCE(ctx->HasOutput("Out"),
|
||||||
|
"Output(Out) of SequenceProjectOp should not be null.");
|
||||||
|
auto in_dims = ctx->GetInputDim("X");
|
||||||
|
PADDLE_ENFORCE(in_dims.size() == 2, "Input(X) should be 2-D tensor.");
|
||||||
|
|
||||||
|
int context_length = ctx->Attrs().Get<int>("context_length");
|
||||||
|
bool padding_trainable = ctx->Attrs().Get<bool>("padding_trainable");
|
||||||
|
int context_start = ctx->Attrs().Get<int>("context_start");
|
||||||
|
|
||||||
|
if (padding_trainable) {
|
||||||
|
PADDLE_ENFORCE(
|
||||||
|
ctx->HasInput("PaddingData"),
|
||||||
|
"Output(PaddingData) of SequenceProjectOp should not be null.");
|
||||||
|
framework::DDim padding_dim = ctx->GetOutputDim("PaddingData");
|
||||||
|
int up_pad = std::max(0, -context_start);
|
||||||
|
int down_pad = std::max(0, context_start + context_length - 1);
|
||||||
|
int total_pad = up_pad + down_pad;
|
||||||
|
int input_width = static_cast<int>(in_dims[1]);
|
||||||
|
|
||||||
|
PADDLE_ENFORCE(padding_dim.size() == 2,
|
||||||
|
"Input(PaddingData) should be 2-D tensor.");
|
||||||
|
PADDLE_ENFORCE(
|
||||||
|
padding_dim[0] == total_pad && padding_dim[1] == input_width,
|
||||||
|
"Input(PaddingData)'s shape is not consistent with 'context_start' "
|
||||||
|
"and 'context_length'.");
|
||||||
|
|
||||||
|
if (context_start == 0 && context_length == 1) {
|
||||||
|
PADDLE_THROW(
|
||||||
|
"if context_start == 0 && context_length == 1, padding_trainable "
|
||||||
|
"should be false.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
in_dims[1] = in_dims[1] * context_length;
|
||||||
|
ctx->SetOutputDim("Out", in_dims);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class SequenceProjectGradOp : public framework::OperatorWithKernel {
|
||||||
|
public:
|
||||||
|
using framework::OperatorWithKernel::OperatorWithKernel;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void InferShape(framework::InferShapeContext* ctx) const override {
|
||||||
|
PADDLE_ENFORCE(ctx->HasInput(framework::GradVarName("Out")),
|
||||||
|
"Gradient of Out should not be null.");
|
||||||
|
PADDLE_ENFORCE(ctx->HasInput("X"), "The input X should not be null.");
|
||||||
|
|
||||||
|
if (ctx->Attrs().Get<bool>("padding_trainable")) {
|
||||||
|
PADDLE_ENFORCE(
|
||||||
|
ctx->HasOutput("PaddingData"),
|
||||||
|
"Output(PaddingData) of SequenceProjectOp should not be null.");
|
||||||
|
}
|
||||||
|
ctx->SetOutputDim(framework::GradVarName("X"), ctx->GetInputDim("X"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class SequenceProjectOpMaker : public framework::OpProtoAndCheckerMaker {
|
||||||
|
public:
|
||||||
|
SequenceProjectOpMaker(framework::OpProto* proto,
|
||||||
|
framework::OpAttrChecker* op_checker)
|
||||||
|
: OpProtoAndCheckerMaker(proto, op_checker) {
|
||||||
|
AddInput(
|
||||||
|
"X",
|
||||||
|
"A float LoDTensor, the variable-length input of SequenceProjectOp");
|
||||||
|
AddOutput(
|
||||||
|
"Out",
|
||||||
|
"A float LoDTensor, the variable-length output of SequenceProjectOp.");
|
||||||
|
AddOutput("PaddingData",
|
||||||
|
"A float LoDTensor, the padding data of SequenceProjectOp.");
|
||||||
|
|
||||||
|
AddAttr<bool>("padding_trainable",
|
||||||
|
"(bool, default false) the padding data of SequenceProjectOp "
|
||||||
|
"is trainable or not.")
|
||||||
|
.SetDefault(false);
|
||||||
|
AddAttr<int>("context_length",
|
||||||
|
"(int, default 3) the stride of SequenceProjectOp.")
|
||||||
|
.SetDefault(3)
|
||||||
|
.GreaterThan(0);
|
||||||
|
AddAttr<int>("context_start",
|
||||||
|
"(int, default 0) the xx of SequenceProjectOp.")
|
||||||
|
.SetDefault(0);
|
||||||
|
AddAttr<int>("context_stride",
|
||||||
|
"(int, default 1) the xx of SequenceProjectOp.")
|
||||||
|
.SetDefault(1)
|
||||||
|
.GreaterThan(0);
|
||||||
|
|
||||||
|
AddComment(R"DOC(
|
||||||
|
SequenceProjectOp projects features of context_length time-steps of each instance.
|
||||||
|
|
||||||
|
For a mini-batch of 2 variable lengths sentences, containing 3, and 1 time-steps:
|
||||||
|
|
||||||
|
Assumed input (X) is a [4, M, N] float LoDTensor, and X->lod()[0] = [0, 3, 4].
|
||||||
|
Besides, for the sake of simplicity, we assume M=1 and N=2.
|
||||||
|
|
||||||
|
X = [[a1, a2,
|
||||||
|
b1, b2.
|
||||||
|
c1, c2]
|
||||||
|
[d1, d2]]
|
||||||
|
|
||||||
|
This is to say that input (X) has 4 words and the dimension of each word
|
||||||
|
representation is 2.
|
||||||
|
|
||||||
|
- Case1:
|
||||||
|
If we use zero to pad instead of learned weight to pad,
|
||||||
|
and the context_lenth is 3, the output (Out) is:
|
||||||
|
|
||||||
|
Out = [0, 0, a1, a2, b1, b2;
|
||||||
|
a1, a2, b1, b2, c1, c2;
|
||||||
|
b1, b2, c1, c2, 0, 0;
|
||||||
|
0, 0, d1, d2, 0, 0]
|
||||||
|
|
||||||
|
- Case2:
|
||||||
|
// If we use zero to pad instead of learned weight to pad,
|
||||||
|
// and the context_lenth is 3, the output (Out) is:
|
||||||
|
//
|
||||||
|
// Out = [0, 0, a1, a2, b1, b2;
|
||||||
|
// a1, a2, b1, b2, c1, c2;
|
||||||
|
// b1, b2, c1, c2, 0, 0;
|
||||||
|
// 0, 0, d1, d2, 0, 0]
|
||||||
|
|
||||||
|
)DOC");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace operators
|
||||||
|
} // namespace paddle
|
||||||
|
|
||||||
|
namespace ops = paddle::operators;
|
||||||
|
REGISTER_OP(sequence_project, ops::SequenceProjectOp,
|
||||||
|
ops::SequenceProjectOpMaker, sequence_project_grad,
|
||||||
|
ops::SequenceProjectGradOp);
|
||||||
|
|
||||||
|
REGISTER_OP_CPU_KERNEL(
|
||||||
|
sequence_project,
|
||||||
|
ops::SequenceProjectKernel<paddle::platform::CPUPlace, float>);
|
||||||
|
REGISTER_OP_CPU_KERNEL(
|
||||||
|
sequence_project_grad,
|
||||||
|
ops::SequenceProjectGradKernel<paddle::platform::CPUPlace, float>);
|
@ -0,0 +1,25 @@
|
|||||||
|
/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License. */
|
||||||
|
|
||||||
|
#define EIGEN_USE_GPU
|
||||||
|
|
||||||
|
#include "paddle/operators/sequence_project_op.h"
|
||||||
|
|
||||||
|
namespace ops = paddle::operators;
|
||||||
|
REGISTER_OP_GPU_KERNEL(
|
||||||
|
sequence_project,
|
||||||
|
ops::SequenceProjectKernel<paddle::platform::GPUPlace, float>);
|
||||||
|
REGISTER_OP_GPU_KERNEL(
|
||||||
|
sequence_project_grad,
|
||||||
|
ops::SequenceProjectGradKernel<paddle::platform::GPUPlace, float>);
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,96 @@
|
|||||||
|
import unittest
|
||||||
|
import numpy as np
|
||||||
|
from op_test import OpTest
|
||||||
|
|
||||||
|
|
||||||
|
class TestSeqProject(OpTest):
|
||||||
|
def setUp(self):
|
||||||
|
self.init_test_case()
|
||||||
|
self.op_type = 'sequence_project'
|
||||||
|
# one level, batch size
|
||||||
|
x = np.random.uniform(
|
||||||
|
0.1, 1, [self.input_size[0], self.input_size[1]]).astype('float32')
|
||||||
|
lod = [[0, 4, 5, 8, self.input_size[0]]]
|
||||||
|
|
||||||
|
self.begin_pad = np.max([0, -self.context_start])
|
||||||
|
self.end_pad = np.max([0, self.context_start + self.context_length - 1])
|
||||||
|
self.total_pad = self.begin_pad + self.end_pad
|
||||||
|
w = np.ones((self.total_pad, self.input_size[1])) * 100
|
||||||
|
|
||||||
|
self.inputs = {'X': (x, lod), 'PaddingData': w}
|
||||||
|
self.attrs = {
|
||||||
|
'context_start': self.context_start,
|
||||||
|
'context_length': self.context_length,
|
||||||
|
'padding_trainable': self.padding_trainable
|
||||||
|
}
|
||||||
|
out = np.zeros((self.input_size[0], self.input_size[1] *
|
||||||
|
self.context_length)).astype('float32')
|
||||||
|
self.outputs = {'Out': out}
|
||||||
|
self.compute()
|
||||||
|
|
||||||
|
def compute(self):
|
||||||
|
x, lod = self.inputs['X']
|
||||||
|
w = self.inputs['PaddingData']
|
||||||
|
out = self.outputs['Out']
|
||||||
|
lod = lod[0]
|
||||||
|
|
||||||
|
for i in range(len(lod) - 1):
|
||||||
|
for j in range(self.context_length):
|
||||||
|
in_begin = lod[i] + self.context_start + j
|
||||||
|
in_end = lod[i + 1] + self.context_start + j
|
||||||
|
out_begin = lod[i]
|
||||||
|
out_end = lod[i + 1]
|
||||||
|
if in_begin < lod[i]:
|
||||||
|
pad_size = np.min([lod[i] - in_begin, lod[i + 1] - lod[i]])
|
||||||
|
if self.padding_trainable:
|
||||||
|
sub_w = w[j:pad_size, :]
|
||||||
|
out[lod[i]:lod[i] + pad_size, j * self.input_size[1]:(
|
||||||
|
j + 1) * self.input_size[1]] = sub_w
|
||||||
|
# pass
|
||||||
|
out_begin = lod[i] + pad_size
|
||||||
|
in_begin = lod[i]
|
||||||
|
|
||||||
|
if in_end > lod[i + 1]:
|
||||||
|
pad_size = np.min(
|
||||||
|
[in_end - lod[i + 1], lod[i + 1] - lod[i]])
|
||||||
|
out_sub = out[lod[i + 1] - pad_size:lod[i + 1], :]
|
||||||
|
if self.padding_trainable:
|
||||||
|
sub_w = w[j - pad_size:j, :]
|
||||||
|
out[lod[i + 1] - pad_size:lod[i + 1], j * self.
|
||||||
|
input_size[1]:(j + 1) * self.input_size[1]] = sub_w
|
||||||
|
# pass
|
||||||
|
in_end = lod[i + 1]
|
||||||
|
out_end = lod[i + 1] - pad_size
|
||||||
|
if in_end <= in_begin:
|
||||||
|
continue
|
||||||
|
|
||||||
|
in_sub = x[in_begin:in_end, :]
|
||||||
|
out[out_begin:out_end, j * self.input_size[1]:(j + 1) *
|
||||||
|
self.input_size[1]] += in_sub
|
||||||
|
|
||||||
|
def init_test_case(self):
|
||||||
|
self.input_size = [11, 23]
|
||||||
|
self.op_type = "sequence_project"
|
||||||
|
|
||||||
|
self.context_start = -1
|
||||||
|
self.context_length = 3
|
||||||
|
self.padding_trainable = False
|
||||||
|
|
||||||
|
def test_check_output(self):
|
||||||
|
self.check_output()
|
||||||
|
|
||||||
|
# def test_check_grad(self):
|
||||||
|
# self.check_grad(["X"], "Out")
|
||||||
|
|
||||||
|
# class TestSeqAvgPool2D(TestSeqProject):
|
||||||
|
# def init_test_case(self):
|
||||||
|
# self.input_size = [11, 23]
|
||||||
|
# self.op_type = "sequence_project"
|
||||||
|
#
|
||||||
|
# self.context_start = -1
|
||||||
|
# self.context_length = 3
|
||||||
|
# self.padding_trainable = True
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
Loading…
Reference in new issue