未验证 提交 e5d810b9 编写于 作者: Y Yancey 提交者: GitHub

Fix seq concat op with refactoring LoD (#5486)

* fix seq_concat with refactaring LoD

* fix failed unit test

* rename function name
上级 f5dca05c
...@@ -68,38 +68,42 @@ class SequenceConcatOpMaker : public framework::OpProtoAndCheckerMaker { ...@@ -68,38 +68,42 @@ class SequenceConcatOpMaker : public framework::OpProtoAndCheckerMaker {
"The level should be less than the level number of inputs.") "The level should be less than the level number of inputs.")
.SetDefault(0); .SetDefault(0);
AddComment(R"DOC( AddComment(R"DOC(
Sequence Concat Operator. The sequence_concat operator concatenates multiple LoDTensors.
It only supports sequence (LoD Tensor with level number is 1)
The sequence_concat operator concatenates multiple LoDTensors.
It supports a sequence (LoD Tensor with level number is 1)
or a nested sequence (LoD tensor with level number is 2) as its input. or a nested sequence (LoD tensor with level number is 2) as its input.
The following examples explain how the operator works:
- Case1: - Case1:
If the axis is other than 0(here, axis is 1 and level is 1), If the axis is other than 0(here, axis is 1 and level is 1),
each input should have the same LoD information and the LoD each input should have the same LoD information and the LoD
information of the output keeps the same as the input. information of the output keeps the same as the input.
LoD(x0) = {{0,2,4}, {0,1,2,3,4}}; Dims(x0) = (4,3,4) LoD(x0) = {{0,2,4}, {0,1,2,3,4}}; Dims(x0) = (4,3,4)
LoD(x1) = {{0,2,4}, {0,1,2,3,4}}; Dims(x1) = (4,4,4) LoD(x1) = {{0,2,4}, {0,1,2,3,4}}; Dims(x1) = (4,4,4)
LoD(Out) = {{0,2,4}, {0,1,2,3,4}}; Dims(Out) = (4,7,4) LoD(Out) = {{0,2,4}, {0,1,2,3,4}}; Dims(Out) = (4,7,4)
- Case2: - Case2:
If the axis is 0(here, leve is 0), the inputs are concatenated along If the axis is 0(here, leve is 0), the inputs are concatenated along
time steps, the LoD information of the output need to re-compute. time steps, the LoD information of the output need to re-compute.
The LoD information of level-1 should be same.
LoD(x0) = {{0,2,4}, {0,1,2,3,4}}; Dims(x0) = (4,3,4) LoD(x0) = {{0,2,4}, {0,1,2,3,4}}; Dims(x0) = (4,3,4)
LoD(x1) = {{0,3,5}, {0,1,2,3,5}}; Dims(x1) = (5,3,4) LoD(x1) = {{0,2,4}, {0,1,3,5,7}}; Dims(x1) = (7,3,4)
LoD(Out) = {{0,5,9}, {0,1,2,3,4,5,6,7,9}}; Dims(Out) = (9,3,4) LoD(Out) = {{0,2,4}, {0,2,5,8,11}}; Dims(Out) = (11,3,4)
- Case3: - Case3:
If the axis is 0(here, level is 1). If the axis is 0(here, level is 1).
LoD(x0) = {{0,2,4}, {0,1,2,3,4}}; Dims(x0) = (4,3,4) LoD(x0) = {{0,2,4}, {0,1,2,3,4}}; Dims(x0) = (4,3,4)
LoD(x1) = {{0,3,5}, {0,1,3,4,5}}; Dims(x1) = (5,3,4) LoD(x1) = {{0,3,4}, {0,1,3,5,7}}; Dims(x1) = (7,3,4)
LoD(Out) = {{0,5,9}, {0,2,5,7,9}}; Dims(Out) = (9,3,4) LoD(Out) = {{0,5,8}, {0,1,2,3,5,7,8,9,11}}; Dims(Out) = (11,3,4)
NOTE: The levels of all the inputs should be the same. - Case4:
If the LoD number is 1, axis is 0, level is 0
LoD(x0) = {{0,1,2,3,4}}; Dims(x0) = (4,3,4)
LoD(x1) = {{0,1,3,5,7}}; Dims(x1) = (7,3,4)
LoD(Out) = {{0,2,5,8,11}}; Dims(Out) = (11,3,4)
NOTE: The levels of all the inputs should be the same.
)DOC"); )DOC");
} }
}; };
......
...@@ -24,28 +24,38 @@ using LoDTensor = framework::LoDTensor; ...@@ -24,28 +24,38 @@ using LoDTensor = framework::LoDTensor;
using LoD = framework::LoD; using LoD = framework::LoD;
template <typename T> template <typename T>
LoD concatLoD(const std::vector<const T*> ins, const size_t axis, LoD ConcatLoD(const std::vector<const T*> ins, const size_t level) {
const size_t level) {
auto out_lod = ins[0]->lod(); auto out_lod = ins[0]->lod();
auto numLevels = ins[0]->NumLevels();
const size_t n = ins.size(); const size_t n = ins.size();
if (axis == 0UL) { const size_t level_idx = ins[0]->NumLevels() - 1 - level;
for (size_t i = 1; i < n; ++i) { for (size_t i = 1; i < n; ++i) {
for (size_t j = 0; j < ins[i]->lod()[0].size(); ++j) { for (size_t j = 0; j < ins[i]->lod()[level_idx].size(); ++j) {
out_lod[0][j] += ins[i]->lod()[0][j]; out_lod[level_idx][j] += ins[i]->lod()[level_idx][j];
} }
}
if (ins[0]->NumLevels() == 2) { for (size_t i = level_idx; i < numLevels - 1; ++i) {
for (size_t j = 1; j < ins[i]->lod()[1].size(); ++j) { size_t lod_len = 1;
if (level == 0UL) { for (size_t j = 0; j < n; ++j) {
out_lod[1].push_back(out_lod[1].back() + ins[i]->lod()[1][j] - lod_len += ins[j]->lod()[i + 1].size() - 1;
ins[i]->lod()[1][j - 1]); }
} else if (level == 1UL) { out_lod[i + 1].clear();
out_lod[1][j] += ins[1]->lod()[1][j]; out_lod[i + 1].resize(lod_len);
}
size_t idx = 1;
for (size_t j = 0; j < ins[0]->lod()[i].size() - 1; ++j) {
for (size_t k = 0; k < n; ++k) {
for (size_t m = ins[k]->lod()[i][j]; m < ins[k]->lod()[i][j + 1]; ++m) {
out_lod[i + 1][idx] = out_lod[i + 1][idx - 1] +
ins[k]->lod()[i + 1][m + 1] -
ins[k]->lod()[i + 1][m];
idx++;
} }
} }
} }
} }
return out_lod; return out_lod;
} }
...@@ -82,18 +92,21 @@ class SequenceConcatOpKernel : public framework::OpKernel<T> { ...@@ -82,18 +92,21 @@ class SequenceConcatOpKernel : public framework::OpKernel<T> {
"should be greater than the specify level"); "should be greater than the specify level");
out->mutable_data<T>(ctx.GetPlace()); out->mutable_data<T>(ctx.GetPlace());
auto out_lod = concatLoD<LoDTensor>(ins, axis, level); auto out_lod = ins[0]->lod();
if (axis == 0) {
out_lod = ConcatLoD<LoDTensor>(ins, level);
}
out->set_lod(out_lod); out->set_lod(out_lod);
auto out_lod_level = out_lod[level]; const size_t level_idx = out_lod.size() - level - 1;
auto out_lod_level = framework::ToAbsOffset(out_lod)[level_idx];
for (size_t i = 0; i < out_lod_level.size() - 1; ++i) { for (size_t i = 0; i < out_lod_level.size() - 1; ++i) {
Tensor out_t = out->Slice(static_cast<int>(out_lod_level[i]), Tensor out_t = out->Slice(static_cast<int>(out_lod_level[i]),
static_cast<int>(out_lod_level[i + 1])); static_cast<int>(out_lod_level[i + 1]));
auto out_stride = framework::stride(out_t.dims()); auto out_stride = framework::stride(out_t.dims());
size_t offset = 0; size_t offset = 0;
for (size_t j = 0; j < n; ++j) { for (size_t j = 0; j < n; ++j) {
auto in_lod_level = ins[j]->lod()[level]; auto in_lod_level = framework::ToAbsOffset(ins[j]->lod())[level_idx];
auto in_stride = framework::stride(ins[j]->dims()); auto in_stride = framework::stride(ins[j]->dims());
Tensor in_t = ins[j]->Slice(static_cast<int>(in_lod_level[i]), Tensor in_t = ins[j]->Slice(static_cast<int>(in_lod_level[i]),
static_cast<int>(in_lod_level[i + 1])); static_cast<int>(in_lod_level[i + 1]));
...@@ -124,9 +137,12 @@ class SequenceConcatGradOpKernel : public framework::OpKernel<T> { ...@@ -124,9 +137,12 @@ class SequenceConcatGradOpKernel : public framework::OpKernel<T> {
x_grads[i]->set_lod(ins[i]->lod()); x_grads[i]->set_lod(ins[i]->lod());
x_grads[i]->mutable_data<T>(ctx.GetPlace()); x_grads[i]->mutable_data<T>(ctx.GetPlace());
} }
auto out_lod = ins[0]->lod();
auto out_lod = concatLoD<LoDTensor>(ins, axis, level); if (axis == 0UL) {
auto out_lod_level = out_lod[level]; out_lod = ConcatLoD<LoDTensor>(ins, level);
}
const size_t level_idx = out_lod.size() - level - 1;
auto out_lod_level = framework::ToAbsOffset(out_lod)[level_idx];
for (size_t i = 0; i < out_lod_level.size() - 1; ++i) { for (size_t i = 0; i < out_lod_level.size() - 1; ++i) {
Tensor out_grad_t = Tensor out_grad_t =
...@@ -136,7 +152,8 @@ class SequenceConcatGradOpKernel : public framework::OpKernel<T> { ...@@ -136,7 +152,8 @@ class SequenceConcatGradOpKernel : public framework::OpKernel<T> {
size_t offset = 0; size_t offset = 0;
for (size_t j = 0; j < n; ++j) { for (size_t j = 0; j < n; ++j) {
auto x_grad_lod_level = x_grads[j]->lod()[level]; auto x_grad_lod_level =
framework::ToAbsOffset(x_grads[j]->lod())[level_idx];
auto x_grad_stride = framework::stride(x_grads[j]->dims()); auto x_grad_stride = framework::stride(x_grads[j]->dims());
Tensor x_grad_t = Tensor x_grad_t =
x_grads[j]->Slice(static_cast<int>(x_grad_lod_level[i]), x_grads[j]->Slice(static_cast<int>(x_grad_lod_level[i]),
......
...@@ -215,7 +215,11 @@ class OpTest(unittest.TestCase): ...@@ -215,7 +215,11 @@ class OpTest(unittest.TestCase):
if isinstance(input_vars[var_name], list): if isinstance(input_vars[var_name], list):
for name, np_value in self.inputs[var_name]: for name, np_value in self.inputs[var_name]:
tensor = core.LoDTensor() tensor = core.LoDTensor()
tensor.set(np_value, place) if isinstance(np_value, tuple):
tensor.set(np_value[0], place)
tensor.set_lod(np_value[1])
else:
tensor.set(np_value, place)
feed_map[name] = tensor feed_map[name] = tensor
else: else:
tensor = core.LoDTensor() tensor = core.LoDTensor()
...@@ -236,7 +240,6 @@ class OpTest(unittest.TestCase): ...@@ -236,7 +240,6 @@ class OpTest(unittest.TestCase):
inputs = append_input_output(block, op_proto, self.inputs, True) inputs = append_input_output(block, op_proto, self.inputs, True)
outputs = append_input_output(block, op_proto, self.outputs, False) outputs = append_input_output(block, op_proto, self.outputs, False)
op = block.append_op( op = block.append_op(
type=self.op_type, type=self.op_type,
inputs=inputs, inputs=inputs,
...@@ -397,9 +400,11 @@ class OpTest(unittest.TestCase): ...@@ -397,9 +400,11 @@ class OpTest(unittest.TestCase):
if not isinstance(item[0], basestring): if not isinstance(item[0], basestring):
item = [[param_name] + list(item)] item = [[param_name] + list(item)]
if len(item) == 2: if len(item) == 2:
# only set var name and value, set lod to None if isinstance(item[1], tuple):
var[i] = list(item) + [None] var[i] = [item[0], item[1][0], item[1][1]]
else:
# only set var name and value, set lod to None
var[i] = list(item) + [None]
var_descs = [(block.create_var( var_descs = [(block.create_var(
name=name, shape=each.shape, dtype=each.dtype), each, lod) name=name, shape=each.shape, dtype=each.dtype), each, lod)
for name, each, lod in var] for name, each, lod in var]
......
...@@ -4,7 +4,33 @@ import sys ...@@ -4,7 +4,33 @@ import sys
from op_test import OpTest from op_test import OpTest
class TestConcatOp(OpTest): def to_abs_lod(lod):
if len(lod) == 0 or len(lod) == 1:
return lod
import copy
new_lod = copy.deepcopy(lod)
for idx, val in enumerate(lod[0]):
new_lod[0][idx] = lod[1][val]
return new_lod
def seq_concat(inputs, level):
lod0 = inputs['X'][0][1][1]
lod1 = inputs['X'][1][1][1]
x0 = inputs['X'][0][1][0]
x1 = inputs['X'][1][1][0]
level_idx = len(lod0) - level - 1
outs = []
for i in range(len(lod0[level_idx]) - 1):
sub_x0 = x0[to_abs_lod(lod0)[level_idx][i]:to_abs_lod(lod0)[level_idx][
i + 1], :]
sub_x1 = x1[to_abs_lod(lod1)[level_idx][i]:to_abs_lod(lod1)[level_idx][
i + 1], :]
outs.append(np.concatenate((sub_x0, sub_x1), axis=0))
return np.concatenate(outs, axis=0)
class TestSeqConcatOp(OpTest):
def set_data(self): def set_data(self):
# two level, batch size is 3 # two level, batch size is 3
x0 = np.random.random((4, 6, 3)).astype('float32') x0 = np.random.random((4, 6, 3)).astype('float32')
...@@ -15,13 +41,7 @@ class TestConcatOp(OpTest): ...@@ -15,13 +41,7 @@ class TestConcatOp(OpTest):
level = 1 level = 1
self.inputs = {'X': [('x0', (x0, lod0)), ('x1', (x1, lod1))]} self.inputs = {'X': [('x0', (x0, lod0)), ('x1', (x1, lod1))]}
self.attrs = {'axis': axis, 'level': level} self.attrs = {'axis': axis, 'level': level}
outs = [] self.outputs = {'Out': (np.concatenate([x0, x1], axis=1), lod0)}
for i in range(4):
sub_x0 = x0[lod0[level][i]:lod0[level][i + 1], :]
sub_x1 = x1[lod1[level][i]:lod1[level][i + 1], :]
outs.append(np.concatenate((sub_x0, sub_x1), axis=axis))
self.outputs = {'Out': np.concatenate(outs, axis=0)}
def setUp(self): def setUp(self):
self.op_type = "sequence_concat" self.op_type = "sequence_concat"
...@@ -34,46 +54,50 @@ class TestConcatOp(OpTest): ...@@ -34,46 +54,50 @@ class TestConcatOp(OpTest):
self.check_grad(['x0'], 'Out') self.check_grad(['x0'], 'Out')
class TestConcatOpDiffLod(TestConcatOp): class TestSeqConcatOpLevelZeroNestedSequence(TestSeqConcatOp):
def set_data(self): def set_data(self):
# two level, batch size is 3 # two level, batch size is 3
x0 = np.random.random((4, 6, 3)).astype('float32') x0 = np.random.random((4, 6, 3)).astype('float32')
lod0 = [[0, 2, 4], [0, 1, 2, 3, 4]] lod0 = [[0, 2, 4], [0, 1, 2, 3, 4]]
x1 = np.random.random((5, 6, 3)).astype('float32') x1 = np.random.random((7, 6, 3)).astype('float32')
lod1 = [[0, 3, 5], [0, 1, 2, 3, 5]] lod1 = [[0, 2, 4], [0, 1, 3, 5, 7]]
axis = 0 axis = 0
level = 1 level = 0
self.inputs = {'X': [('x0', (x0, lod0)), ('x1', (x1, lod1))]} self.inputs = {'X': [('x0', (x0, lod0)), ('x1', (x1, lod1))]}
self.attrs = {'axis': axis, 'level': level} self.attrs = {'axis': axis, 'level': level}
outs = [] out_lod = [[0, 2, 4], [0, 2, 5, 8, 11]]
for i in range(4): self.outputs = {'Out': (seq_concat(self.inputs, level), out_lod)}
sub_x0 = x0[lod0[level][i]:lod0[level][i + 1], :]
sub_x1 = x1[lod1[level][i]:lod1[level][i + 1], :]
outs.append(np.concatenate((sub_x0, sub_x1), axis=axis))
self.outputs = {'Out': np.concatenate(outs, axis=0)}
class TestSeqConcatOplevelOneNestedSequence(TestSeqConcatOp):
def set_data(self):
# two level, batch size is 3
x0 = np.random.random((4, 6, 3)).astype('float32')
lod0 = [[0, 2, 4], [0, 1, 2, 3, 4]]
x1 = np.random.random((7, 6, 3)).astype('float32')
lod1 = [[0, 3, 4], [0, 1, 3, 5, 7]]
axis = 0
level = 1
self.inputs = {'X': [('x0', (x0, lod0)), ('x1', (x1, lod1))]}
self.attrs = {'axis': axis, 'level': level}
out_lod = [[0, 5, 8], [0, 1, 2, 3, 5, 7, 8, 9, 11]]
self.outputs = {'Out': (seq_concat(self.inputs, level), out_lod)}
class TestConcatOpLevelZero(TestConcatOp): class TestSeqConcatOpLevelZeroSequence(TestSeqConcatOp):
def set_data(self): def set_data(self):
# two level, batch size is 3 # two level, batch size is 3
x0 = np.random.random((4, 3, 4)).astype('float32') x0 = np.random.random((4, 3, 4)).astype('float32')
lod0 = [[0, 2, 4], [0, 1, 2, 3, 4]] lod0 = [[0, 1, 2, 3, 4]]
x1 = np.random.random((5, 3, 4)).astype('float32') x1 = np.random.random((7, 3, 4)).astype('float32')
lod1 = [[0, 3, 5], [0, 1, 3, 4, 5]] lod1 = [[0, 1, 3, 5, 7]]
axis = 0 axis = 0
level = 0 level = 0
self.inputs = {'X': [('x0', (x0, lod0)), ('x1', (x1, lod1))]} self.inputs = {'X': [('x0', (x0, lod0)), ('x1', (x1, lod1))]}
self.attrs = {'axis': axis, 'level': level} self.attrs = {'axis': axis, 'level': level}
outs = [] out_lod = [[0, 2, 5, 8, 11]]
for i in range(2): self.outputs = {'Out': (seq_concat(self.inputs, level), out_lod)}
sub_x0 = x0[lod0[level][i]:lod0[level][i + 1], :]
sub_x1 = x1[lod1[level][i]:lod1[level][i + 1], :]
outs.append(np.concatenate((sub_x0, sub_x1), axis=axis))
self.outputs = {'Out': np.concatenate(outs, axis=0)}
if __name__ == '__main__': if __name__ == '__main__':
sys.exit(0)
unittest.main() unittest.main()
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册