test_sequence_conv.py 9.5 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.

C
chengduoZH 已提交
15
import random
16
import sys
17 18 19
import unittest

import numpy as np
20

G
GGBond8488 已提交
21 22
import paddle

23
sys.path.append("../")
24
from op_test import OpTest
C
chengduoZH 已提交
25 26


27 28 29 30 31 32 33 34 35
def seqconv(
    x,
    lod,
    filter,
    context_length,
    context_start,
    padding_trainable=False,
    padding_data=None,
):
36 37 38 39 40 41 42 43 44 45 46 47 48 49
    [T, M] = x.shape
    col = np.zeros((T, context_length * M)).astype('float32')
    offset = [0]
    for seq_len in lod[0]:
        offset.append(offset[-1] + seq_len)
    begin_pad = np.max([0, -context_start])
    for i in range(len(offset) - 1):
        for j in range(context_length):
            in_begin = offset[i] + context_start + j
            in_end = offset[i + 1] + context_start + j
            out_begin = offset[i]
            out_end = offset[i + 1]
            if in_begin < offset[i]:
                pad_size = np.min(
50 51
                    [offset[i] - in_begin, offset[i + 1] - offset[i]]
                )
52
                if padding_trainable:
53 54 55 56
                    sub_w = padding_data[j : j + pad_size, :]
                    col[
                        offset[i] : offset[i] + pad_size, j * M : (j + 1) * M
                    ] = sub_w
57 58 59 60 61
                out_begin = offset[i] + pad_size
                in_begin = offset[i]

            if in_end > offset[i + 1]:
                pad_size = np.min(
62 63
                    [in_end - offset[i + 1], offset[i + 1] - offset[i]]
                )
64
                if padding_trainable:
65 66 67 68 69 70 71 72 73 74 75 76 77
                    sub_w = padding_data[
                        begin_pad
                        + context_start
                        + j
                        - pad_size : begin_pad
                        + context_start
                        + j,
                        :,
                    ]
                    col[
                        offset[i + 1] - pad_size : offset[i + 1],
                        j * M : (j + 1) * M,
                    ] = sub_w
78 79 80 81 82
                in_end = offset[i + 1]
                out_end = offset[i + 1] - pad_size
            if in_end <= in_begin:
                continue
            in_sub = x[in_begin:in_end, :]
83
            col[out_begin:out_end, j * M : (j + 1) * M] += in_sub
84 85 86
    return np.dot(col, filter)


C
chengduoZH 已提交
87 88 89 90 91
class TestSeqProject(OpTest):
    def setUp(self):
        self.init_test_case()
        self.op_type = 'sequence_conv'

92 93 94 95 96 97 98 99 100 101
        if (
            self.context_length == 1
            and self.context_start == 0
            and self.padding_trainable
        ):
            print(
                "If context_start is 0 "
                "and context_length is 1,"
                " padding_trainable should be false."
            )
C
chengduoZH 已提交
102 103 104
            return

        # one level, batch size
105
        x = np.random.uniform(
106 107 108 109 110 111 112 113 114 115
            0.1, 1, [self.input_size[0], self.input_size[1]]
        ).astype('float32')
        w = np.random.uniform(
            0.1,
            1,
            [
                self.context_length * self.input_size[1],
                self.output_represention,
            ],
        ).astype('float32')
C
chengduoZH 已提交
116 117 118 119 120

        begin_pad = np.max([0, -self.context_start])
        end_pad = np.max([0, self.context_start + self.context_length - 1])
        total_pad = begin_pad + end_pad
        padding_data = np.random.uniform(
121 122
            0.1, 1, [total_pad, self.input_size[1]]
        ).astype('float32')
C
chengduoZH 已提交
123
        self.pad_data = padding_data
C
chengduoZH 已提交
124 125
        self.inputs = {
            'X': (x, self.lod),
C
chengduoZH 已提交
126
            'Filter': w,
C
chengduoZH 已提交
127
        }
C
chengduoZH 已提交
128 129 130 131 132 133 134 135 136 137
        self.inputs_val = ['X', 'Filter']
        self.inputs_val_no_x = ['Filter']
        self.inputs_val_no_f = ['X']

        if total_pad != 0:
            self.inputs['PaddingData'] = padding_data
            self.inputs_val = ['X', 'PaddingData', 'Filter']
            self.inputs_val_no_x = ['PaddingData', 'Filter']
            self.inputs_val_no_f = ['PaddingData', 'X']

C
chengduoZH 已提交
138
        self.attrs = {
C
chengduoZH 已提交
139 140 141
            'contextStart': self.context_start,
            'contextLength': self.context_length,
            'paddingTrainable': self.padding_trainable,
142
            'contextStride': self.context_stride,
C
chengduoZH 已提交
143
        }
144 145 146 147 148 149 150 151 152
        out = seqconv(
            x,
            self.lod,
            w,
            self.context_length,
            self.context_start,
            self.padding_trainable,
            self.pad_data,
        )
C
chengduoZH 已提交
153 154 155 156 157 158 159
        self.outputs = {'Out': out}

    def test_check_output(self):
        self.check_output()

    def test_check_grad(self):
        if self.padding_trainable:
160 161 162
            self.check_grad(
                set(self.inputs_val), 'Out', max_relative_error=0.05
            )
C
chengduoZH 已提交
163 164

    def test_check_grad_input(self):
165 166 167 168 169 170
        self.check_grad(
            ['X'],
            'Out',
            max_relative_error=0.05,
            no_grad_set=set(self.inputs_val_no_x),
        )
C
chengduoZH 已提交
171 172 173

    def test_check_grad_padding_data(self):
        if self.padding_trainable:
174 175 176
            self.check_grad(
                ['PaddingData'], 'Out', no_grad_set=set(['X', 'Filter'])
            )
C
chengduoZH 已提交
177 178

    def test_check_grad_Filter(self):
179 180 181 182 183 184
        self.check_grad(
            ['Filter'],
            'Out',
            max_relative_error=0.05,
            no_grad_set=set(self.inputs_val_no_f),
        )
C
chengduoZH 已提交
185

C
chengduoZH 已提交
186
    def test_check_grad_input_filter(self):
C
chengduoZH 已提交
187
        if self.padding_trainable:
188 189 190 191 192 193
            self.check_grad(
                ['X', 'Filter'],
                'Out',
                max_relative_error=0.05,
                no_grad_set=set(['PaddingData']),
            )
C
chengduoZH 已提交
194 195 196

    def test_check_grad_padding_input(self):
        if self.padding_trainable:
197 198 199 200 201 202
            self.check_grad(
                self.inputs_val_no_f,
                'Out',
                max_relative_error=0.05,
                no_grad_set=set(['Filter']),
            )
C
chengduoZH 已提交
203 204 205

    def test_check_grad_padding_filter(self):
        if self.padding_trainable:
206 207 208 209 210 211
            self.check_grad(
                self.inputs_val_no_x,
                'Out',
                max_relative_error=0.05,
                no_grad_set=set(['X']),
            )
C
chengduoZH 已提交
212

C
chengduoZH 已提交
213 214 215 216 217 218 219 220
    def init_test_case(self):
        self.input_row = 11
        self.context_start = 0
        self.context_length = 1
        self.padding_trainable = False
        self.context_stride = 1

        self.input_size = [self.input_row, 23]
221 222 223 224 225
        offset_lod = [[0, 4, 5, 8, self.input_row]]
        self.lod = [[]]
        # convert from offset-based lod to length-based lod
        for i in range(len(offset_lod[0]) - 1):
            self.lod[0].append(offset_lod[0][i + 1] - offset_lod[0][i])
C
chengduoZH 已提交
226
        self.output_represention = 8  # output feature size
C
chengduoZH 已提交
227 228 229 230 231 232 233 234 235 236


class TestSeqProjectCase1(TestSeqProject):
    def init_test_case(self):
        self.input_row = 11
        self.context_start = -1
        self.context_length = 3
        self.padding_trainable = True
        self.context_stride = 1

Z
zhupengyang 已提交
237
        self.input_size = [self.input_row, 50]
238 239 240 241 242
        offset_lod = [[0, 4, 5, 8, self.input_row]]
        self.lod = [[]]
        # convert from offset-based lod to length-based lod
        for i in range(len(offset_lod[0]) - 1):
            self.lod[0].append(offset_lod[0][i + 1] - offset_lod[0][i])
C
chengduoZH 已提交
243
        self.output_represention = 8  # output feature size
C
chengduoZH 已提交
244 245


246 247 248 249 250 251 252 253
class TestSeqProjectCase2Len0(TestSeqProject):
    def init_test_case(self):
        self.input_row = 11
        self.context_start = -1
        self.context_length = 3
        self.padding_trainable = True
        self.context_stride = 1

Z
zhupengyang 已提交
254
        self.input_size = [self.input_row, 50]
255 256 257 258 259 260 261 262 263
        offset_lod = [[0, 0, 4, 5, 5, 8, self.input_row, self.input_row]]
        self.lod = [[]]
        # convert from offset-based lod to length-based lod
        for i in range(len(offset_lod[0]) - 1):
            self.lod[0].append(offset_lod[0][i + 1] - offset_lod[0][i])
        self.output_represention = 8  # output feature size


class TestSeqProjectCase3(TestSeqProject):
C
chengduoZH 已提交
264 265 266 267 268 269 270
    def init_test_case(self):
        self.input_row = 25
        self.context_start = 2
        self.context_length = 3
        self.padding_trainable = True
        self.context_stride = 1

Z
zhupengyang 已提交
271
        self.input_size = [self.input_row, 25]
272
        idx = list(range(self.input_size[0]))
C
chengduoZH 已提交
273
        del idx[0]
274 275 276
        offset_lod = [
            [0] + np.sort(random.sample(idx, 8)).tolist() + [self.input_size[0]]
        ]
277 278 279 280
        self.lod = [[]]
        # convert from offset-based lod to length-based lod
        for i in range(len(offset_lod[0]) - 1):
            self.lod[0].append(offset_lod[0][i + 1] - offset_lod[0][i])
C
chengduoZH 已提交
281
        self.output_represention = 8  # output feature size
C
chengduoZH 已提交
282 283


284 285 286 287
class TestSeqConvApi(unittest.TestCase):
    def test_api(self):
        import paddle.fluid as fluid

G
GGBond8488 已提交
288
        x = paddle.static.data('x', shape=[-1, 32], lod_level=1)
289
        y = paddle.static.nn.sequence_lod.sequence_conv(
290 291
            input=x, num_filters=2, filter_size=3, padding_start=None
        )
292 293 294

        place = fluid.CPUPlace()
        x_tensor = fluid.create_lod_tensor(
295 296
            np.random.rand(10, 32).astype("float32"), [[2, 3, 1, 4]], place
        )
297 298 299 300 301
        exe = fluid.Executor(place)
        exe.run(fluid.default_startup_program())
        ret = exe.run(feed={'x': x_tensor}, fetch_list=[y], return_numpy=False)


C
chengduoZH 已提交
302 303
if __name__ == '__main__':
    unittest.main()