test_seq_conv.py 8.2 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

C
chengduoZH 已提交
17 18 19
import unittest
import numpy as np
import random
20
from op_test import OpTest
C
chengduoZH 已提交
21 22 23 24 25 26 27 28 29 30


class TestSeqProject(OpTest):
    def setUp(self):
        self.init_test_case()
        self.op_type = 'sequence_conv'

        if self.context_length == 1 \
                and self.context_start == 0 \
                and self.padding_trainable:
31
            print("If context_start is 0 " \
C
chengduoZH 已提交
32
                  "and context_length is 1," \
33
                  " padding_trainable should be false.")
C
chengduoZH 已提交
34 35 36 37 38
            return

        # one level, batch size
        x = np.random.uniform(0.1, 1, [self.input_size[0],
                                       self.input_size[1]]).astype('float32')
C
chengduoZH 已提交
39 40 41
        w = np.random.uniform(0.1, 1, [
            self.context_length * self.input_size[1], self.output_represention
        ]).astype('float32')
C
chengduoZH 已提交
42 43 44 45 46 47 48

        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(
            0.1, 1, [total_pad, self.input_size[1]]).astype('float32')
        self.pad_data = padding_data
C
chengduoZH 已提交
49 50
        self.inputs = {
            'X': (x, self.lod),
C
chengduoZH 已提交
51
            'Filter': w,
C
chengduoZH 已提交
52
        }
C
chengduoZH 已提交
53 54 55 56 57 58 59 60 61 62
        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 已提交
63
        self.attrs = {
C
chengduoZH 已提交
64 65 66 67
            'contextStart': self.context_start,
            'contextLength': self.context_length,
            'paddingTrainable': self.padding_trainable,
            'contextStride': self.context_stride
C
chengduoZH 已提交
68
        }
C
chengduoZH 已提交
69 70
        out = np.zeros(
            (self.input_size[0], self.output_represention)).astype('float32')
C
chengduoZH 已提交
71 72 73 74 75 76
        self.outputs = {'Out': out}
        self.compute()

    def compute(self):
        x, lod = self.inputs['X']
        filter = self.inputs['Filter']
C
chengduoZH 已提交
77
        pading_data = self.pad_data
C
chengduoZH 已提交
78 79
        out = np.zeros((self.input_size[0], self.context_length *
                        self.input_size[1])).astype('float32')
80 81 82
        offset = [0]
        for seq_len in lod[0]:
            offset.append(offset[-1] + seq_len)
C
chengduoZH 已提交
83 84
        begin_pad = np.max([0, -self.context_start])

85
        for i in range(len(offset) - 1):
C
chengduoZH 已提交
86
            for j in range(self.context_length):
87 88 89 90 91 92 93
                in_begin = offset[i] + self.context_start + j
                in_end = offset[i + 1] + self.context_start + j
                out_begin = offset[i]
                out_end = offset[i + 1]
                if in_begin < offset[i]:
                    pad_size = np.min(
                        [offset[i] - in_begin, offset[i + 1] - offset[i]])
C
chengduoZH 已提交
94 95
                    if self.padding_trainable:
                        sub_w = pading_data[j:j + pad_size, :]
96 97 98 99
                        out[offset[i]:offset[i] + pad_size, j * self.input_size[
                            1]:(j + 1) * self.input_size[1]] = sub_w
                    out_begin = offset[i] + pad_size
                    in_begin = offset[i]
C
chengduoZH 已提交
100

101
                if in_end > offset[i + 1]:
C
chengduoZH 已提交
102
                    pad_size = np.min(
103
                        [in_end - offset[i + 1], offset[i + 1] - offset[i]])
C
chengduoZH 已提交
104 105 106 107
                    if self.padding_trainable:
                        sub_w = pading_data[begin_pad + self.context_start + j -
                                            pad_size:begin_pad +
                                            self.context_start + j, :]
108
                        out[offset[i + 1] - pad_size:offset[i + 1], j * self.
C
chengduoZH 已提交
109
                            input_size[1]:(j + 1) * self.input_size[1]] = sub_w
110 111
                    in_end = offset[i + 1]
                    out_end = offset[i + 1] - pad_size
C
chengduoZH 已提交
112 113 114 115 116 117 118
                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

C
chengduoZH 已提交
119
        np.dot(out, filter, out=self.outputs['Out'])
C
chengduoZH 已提交
120 121 122 123 124 125 126

    def test_check_output(self):
        self.check_output()

    def test_check_grad(self):
        if self.padding_trainable:
            self.check_grad(
C
chengduoZH 已提交
127
                set(self.inputs_val), 'Out', max_relative_error=0.05)
C
chengduoZH 已提交
128 129 130 131 132 133

    def test_check_grad_input(self):
        self.check_grad(
            ['X'],
            'Out',
            max_relative_error=0.05,
C
chengduoZH 已提交
134
            no_grad_set=set(self.inputs_val_no_x))
C
chengduoZH 已提交
135 136 137 138 139 140 141 142 143 144 145 146 147 148

    def test_check_grad_padding_data(self):
        if self.padding_trainable:
            self.check_grad(
                ['PaddingData'],
                'Out',
                max_relative_error=0.05,
                no_grad_set=set(['X', 'Filter']))

    def test_check_grad_Filter(self):
        self.check_grad(
            ['Filter'],
            'Out',
            max_relative_error=0.05,
C
chengduoZH 已提交
149
            no_grad_set=set(self.inputs_val_no_f))
C
chengduoZH 已提交
150

C
chengduoZH 已提交
151
    def test_check_grad_input_filter(self):
C
chengduoZH 已提交
152 153 154 155 156 157
        if self.padding_trainable:
            self.check_grad(
                ['X', 'Filter'],
                'Out',
                max_relative_error=0.05,
                no_grad_set=set(['PaddingData']))
C
chengduoZH 已提交
158 159 160 161

    def test_check_grad_padding_input(self):
        if self.padding_trainable:
            self.check_grad(
C
chengduoZH 已提交
162
                self.inputs_val_no_f,
C
chengduoZH 已提交
163 164 165 166 167 168 169
                'Out',
                max_relative_error=0.05,
                no_grad_set=set(['Filter']))

    def test_check_grad_padding_filter(self):
        if self.padding_trainable:
            self.check_grad(
C
chengduoZH 已提交
170
                self.inputs_val_no_x,
C
chengduoZH 已提交
171 172 173 174
                'Out',
                max_relative_error=0.05,
                no_grad_set=set(['X']))

C
chengduoZH 已提交
175 176 177 178 179 180 181 182
    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]
183 184 185 186 187
        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 已提交
188
        self.output_represention = 8  # output feature size
C
chengduoZH 已提交
189 190 191 192 193 194 195 196 197 198 199


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

        self.input_size = [self.input_row, 23]
200 201 202 203 204
        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 已提交
205
        self.output_represention = 8  # output feature size
C
chengduoZH 已提交
206 207 208 209 210 211 212 213 214 215 216


class TestSeqProjectCase2(TestSeqProject):
    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

        self.input_size = [self.input_row, 23]
217
        idx = list(range(self.input_size[0]))
C
chengduoZH 已提交
218
        del idx[0]
219 220 221 222 223 224
        offset_lod = [[0] + np.sort(random.sample(idx, 8)).tolist() +
                      [self.input_size[0]]]
        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 已提交
225
        self.output_represention = 8  # output feature size
C
chengduoZH 已提交
226 227 228 229


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