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