test_reorder_lod_tensor.py 8.3 KB
Newer Older
1
#   Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
D
dzhwinter 已提交
2
#
D
dzhwinter 已提交
3 4 5
# 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
D
dzhwinter 已提交
6
#
D
dzhwinter 已提交
7
#     http://www.apache.org/licenses/LICENSE-2.0
D
dzhwinter 已提交
8
#
D
dzhwinter 已提交
9 10 11 12 13 14
# 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.

15 16
from __future__ import print_function

Y
Yang Yu 已提交
17
import unittest
18 19
import paddle.fluid as fluid
import paddle.fluid.core as core
20
from paddle.fluid.layers.control_flow import lod_rank_table
Y
Yang Yu 已提交
21
import numpy
M
minqiyang 已提交
22
import functools
Y
Yang Yu 已提交
23 24


T
tensor-tang 已提交
25 26 27 28 29 30 31 32
def convert_to_offset(lod):
    offset = [[0] for i in lod]
    for i, level in enumerate(lod):
        for seq_len in level:
            offset[i].append(offset[i][-1] + seq_len)
    return offset


Y
Yang Yu 已提交
33
class TestReorderLoDTensor(unittest.TestCase):
34
    num_seq = 5
G
guosheng 已提交
35 36
    # [name, shape, lod_level] pair indicating data info of source and target
    data_desc = (['input', [9], 0], ['ref', [5], 1])
37 38 39 40 41 42 43 44

    @classmethod
    def setUpClass(cls):
        cls.set_program()

    @classmethod
    def set_program(cls):
        dat = fluid.layers.data(
G
guosheng 已提交
45
            name=cls.data_desc[0][0], shape=cls.data_desc[0][1])
Y
Yang Yu 已提交
46
        dat.stop_gradient = False
47
        rank_dat = fluid.layers.data(
G
guosheng 已提交
48
            name=cls.data_desc[1][0], shape=cls.data_desc[1][1])
49
        table = lod_rank_table(rank_dat)
Y
Yang Yu 已提交
50 51
        new_dat = fluid.layers.reorder_lod_tensor_by_rank(
            x=dat, rank_table=table)
52
        loss = fluid.layers.reduce_sum(new_dat)
F
fengjiayi 已提交
53
        fluid.backward.append_backward(loss=loss)
54 55 56 57 58 59
        cls.fetch_list = [new_dat, cls.data_desc[0][0] + '@GRAD']

    def run_program(self):
        outputs = []
        input_grads = []
        places = [core.CPUPlace()]
60
        if core.is_compiled_with_cuda():
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
            places.append(core.CUDAPlace(0))
        for place in places:
            self.set_inputs(place)
            exe = fluid.Executor(place)
            output, input_grad = exe.run(fluid.default_main_program(),
                                         feed=self.inputs,
                                         fetch_list=self.fetch_list,
                                         return_numpy=False)
            outputs.append(output)
            input_grads.append(input_grad)
        self.actual_outputs = outputs
        self.actual_grads = input_grads

    def set_data(self):
        self.data = {}
        for desc in self.data_desc:
            data_name = desc[0]
G
guosheng 已提交
78
            data_shape = desc[1]
79 80 81 82 83 84
            data_lod_level = desc[2]
            data_lod = []
            for i in range(data_lod_level):
                lod_level_i = numpy.random.randint(
                    low=1,
                    high=5,
85
                    size=self.num_seq if i == 0 else sum(lod_level_i)).tolist()
86
                data_lod.append(lod_level_i)
G
guosheng 已提交
87
            data_value = numpy.random.random(
88
                size=[sum(data_lod[-1]) if data_lod else self.num_seq
G
guosheng 已提交
89
                      ] + data_shape).astype('float32')
90 91 92 93 94 95 96 97
            self.data[data_name] = (data_value, data_lod)

    def set_inputs(self, place):
        self.inputs = {}
        for desc in self.data_desc:
            tensor = fluid.Tensor()
            tensor.set(self.data[desc[0]][0], place)
            if self.data[desc[0]][1]:
98
                tensor.set_recursive_sequence_lengths(self.data[desc[0]][1])
99 100 101
            self.inputs[desc[0]] = tensor

    def reorder(self):
102
        level = 0
103 104 105
        # compute the rank_table according to ref_lod
        ref_lod = self.data[self.data_desc[1][0]][1][level]
        rank_table = []  # list of (index, length)
106 107
        for i in range(len(ref_lod)):
            rank_table.append((i, ref_lod[i]))
M
minqiyang 已提交
108 109
        rank_table = sorted(
            rank_table, key=functools.cmp_to_key(lambda x, y: y[1] - x[1]))
110 111 112

        # compute the input sequence info according to input_lod
        input_value, input_lod = self.data[self.data_desc[0][0]]
113
        offset_lod = convert_to_offset(input_lod)
114 115

        input_table = []  # list of (offset, length, sub_lod)
116 117
        if offset_lod:
            for i in range(len(offset_lod[level]) - 1):
118 119 120
                start_idx = i
                end_idx = i + 1
                sub_lod = []
121
                for lod_level_i in offset_lod[level:]:
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
                    sub_lod_i = []
                    for idx in range(start_idx, end_idx):
                        sub_lod_i.append(lod_level_i[idx + 1] - lod_level_i[
                            idx])
                    sub_lod.append(sub_lod_i)
                    start_idx = lod_level_i[start_idx]
                    end_idx = lod_level_i[end_idx]
                input_table.append((start_idx, end_idx - start_idx, sub_lod))
        else:
            input_table = [(i, 1, []) for i in range(len(rank_table))]

        # reorder by rank_table
        output_value = numpy.zeros_like(input_value)
        output_lod = []
        offset = 0
        for index, length in rank_table:
            input_seq_start = input_table[index][0]
            input_seq_len = input_table[index][1]
            input_seq_end = input_seq_start + input_seq_len
            output_value[offset:offset + input_seq_len] = input_value[
                input_seq_start:input_seq_end]
            offset += input_seq_len

            input_seq_sub_lod = input_table[index][2]
            if len(output_lod) == 0:
147 148 149
                output_lod = [[] for i in input_seq_sub_lod]
            for i, level in enumerate(input_seq_sub_lod):
                output_lod[i].extend(level)
150 151 152 153 154 155 156 157 158 159 160 161
        return output_value, output_lod

    def test_reorder_lod_tensor(self):
        self.data_desc[0][-1] = 2  # input is lod_tensor
        self.set_data()
        self.run_program()
        # check output
        expect_output, expect_output_lod = self.reorder()
        for actual_output in self.actual_outputs:
            self.assertTrue(
                numpy.allclose(
                    numpy.array(actual_output), expect_output, atol=0.001))
162 163
            self.assertEqual(expect_output_lod,
                             actual_output.recursive_sequence_lengths())
164 165 166 167 168 169 170
        # check gradient
        expect_grad = numpy.ones_like(self.data[self.data_desc[0][0]][0])
        expect_grad_lod = self.data[self.data_desc[0][0]][1]
        for actual_grad in self.actual_grads:
            self.assertTrue(
                numpy.allclose(
                    numpy.array(actual_grad), expect_grad, atol=0.001))
171 172
            self.assertEqual(expect_grad_lod,
                             actual_grad.recursive_sequence_lengths())
173 174 175 176 177 178 179 180 181 182 183

    def test_reorder_tensor(self):
        self.data_desc[0][-1] = 0  # input is tensor
        self.set_data()
        self.run_program()
        # check output
        expect_output, expect_output_lod = self.reorder()
        for actual_output in self.actual_outputs:
            self.assertTrue(
                numpy.allclose(
                    numpy.array(actual_output), expect_output, atol=0.001))
184 185
            self.assertEqual(expect_output_lod,
                             actual_output.recursive_sequence_lengths())
186 187 188 189 190 191 192
        # check gradient
        expect_grad = numpy.ones_like(self.data[self.data_desc[0][0]][0])
        expect_grad_lod = self.data[self.data_desc[0][0]][1]
        for actual_grad in self.actual_grads:
            self.assertTrue(
                numpy.allclose(
                    numpy.array(actual_grad), expect_grad, atol=0.001))
193 194
            self.assertEqual(expect_grad_lod,
                             actual_grad.recursive_sequence_lengths())
Y
Yang Yu 已提交
195

196 197
        # compare outputs between LodTensors with explicit and implicit lod
        # use the same data but set the input lod explicitly
198 199 200
        input_lod = [[1] * len(self.data[self.data_desc[0][0]][0])]
        self.inputs[self.data_desc[0][0]].set_recursive_sequence_lengths(
            input_lod)
201 202 203 204 205 206 207 208 209
        # preserve the output of LodTensor with implicit lod to compare
        expect_output = [
            numpy.array(actual_output) for actual_output in self.actual_outputs
        ]
        self.run_program()
        for actual_output in self.actual_outputs:
            self.assertTrue(
                numpy.allclose(
                    numpy.array(actual_output), expect_output, atol=0.001))
Y
Yang Yu 已提交
210 211 212 213


if __name__ == '__main__':
    unittest.main()