test_imperative_ptb_rnn.py 15.2 KB
Newer Older
J
JiabinYang 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
#   Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
#
# 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.

import unittest
16 17 18

import numpy as np
from test_imperative_base import new_program_scope
19
from utils import DyGraphProgramDescTracerTestHelper
20

L
Leo Chen 已提交
21
import paddle
J
JiabinYang 已提交
22
import paddle.fluid as fluid
23
import paddle.fluid.core as core
J
JiabinYang 已提交
24
import paddle.fluid.framework as framework
L
lujun 已提交
25
from paddle.fluid.dygraph.base import to_variable
26
from paddle.fluid.optimizer import SGDOptimizer
27
from paddle.nn import Embedding
J
JiabinYang 已提交
28 29


30
class SimpleLSTMRNN(fluid.Layer):
31 32 33
    def __init__(
        self, hidden_size, num_steps, num_layers=2, init_scale=0.1, dropout=None
    ):
34
        super().__init__()
J
JiabinYang 已提交
35 36 37 38
        self._hidden_size = hidden_size
        self._num_layers = num_layers
        self._init_scale = init_scale
        self._dropout = dropout
39 40
        self._input = None
        self._num_steps = num_steps
41 42
        self.cell_array = []
        self.hidden_array = []
43
        self._create_parameter()
J
JiabinYang 已提交
44

45
    def _create_parameter(self):
J
JiabinYang 已提交
46 47 48 49 50 51
        self.weight_1_arr = []
        self.weight_2_arr = []
        self.bias_arr = []
        self.mask_array = []

        for i in range(self._num_layers):
52
            weight_1 = self.create_parameter(
53 54
                attr=fluid.ParamAttr(
                    initializer=fluid.initializer.UniformInitializer(
55 56 57
                        low=-self._init_scale, high=self._init_scale
                    )
                ),
J
JiabinYang 已提交
58 59 60
                shape=[self._hidden_size * 2, self._hidden_size * 4],
                dtype="float32",
                default_initializer=fluid.initializer.UniformInitializer(
61 62 63
                    low=-self._init_scale, high=self._init_scale
                ),
            )
64
            self.weight_1_arr.append(self.add_parameter('w_%d' % i, weight_1))
65
            bias_1 = self.create_parameter(
66 67
                attr=fluid.ParamAttr(
                    initializer=fluid.initializer.UniformInitializer(
68 69 70
                        low=-self._init_scale, high=self._init_scale
                    )
                ),
71
                shape=[self._hidden_size * 4],
J
JiabinYang 已提交
72
                dtype="float32",
73 74
                default_initializer=fluid.initializer.Constant(0.0),
            )
75
            self.bias_arr.append(self.add_parameter('b_%d' % i, bias_1))
J
JiabinYang 已提交
76

77 78 79 80 81
    def forward(self, input_embedding, init_hidden=None, init_cell=None):
        self.cell_array = []
        self.hidden_array = []

        for i in range(self._num_layers):
2
201716010711 已提交
82
            pre_hidden = paddle.slice(
83 84
                init_hidden, axes=[0], starts=[i], ends=[i + 1]
            )
2
201716010711 已提交
85
            pre_cell = paddle.slice(
86 87
                init_cell, axes=[0], starts=[i], ends=[i + 1]
            )
88
            pre_hidden = paddle.reshape(
89 90
                pre_hidden, shape=[-1, self._hidden_size]
            )
91
            pre_cell = paddle.reshape(pre_cell, shape=[-1, self._hidden_size])
J
JiabinYang 已提交
92 93 94 95
            self.hidden_array.append(pre_hidden)
            self.cell_array.append(pre_cell)

        res = []
96
        for index in range(self._num_steps):
2
201716010711 已提交
97
            self._input = paddle.slice(
98 99
                input_embedding, axes=[1], starts=[index], ends=[index + 1]
            )
100
            self._input = paddle.reshape(
101 102
                self._input, shape=[-1, self._hidden_size]
            )
J
JiabinYang 已提交
103 104 105 106 107 108
            for k in range(self._num_layers):
                pre_hidden = self.hidden_array[k]
                pre_cell = self.cell_array[k]
                weight_1 = self.weight_1_arr[k]
                bias = self.bias_arr[k]

109
                nn = fluid.layers.concat([self._input, pre_hidden], 1)
K
kangguangli 已提交
110
                gate_input = paddle.matmul(x=nn, y=weight_1)
J
JiabinYang 已提交
111

112
                gate_input = paddle.add(gate_input, bias)
113 114
                i, j, f, o = paddle.split(
                    gate_input, num_or_sections=4, axis=-1
115
                )
116 117 118 119
                c = pre_cell * paddle.nn.functional.sigmoid(
                    f
                ) + paddle.nn.functional.sigmoid(i) * paddle.tanh(j)
                m = paddle.tanh(c) * paddle.nn.functional.sigmoid(o)
120 121 122 123 124
                self.hidden_array[k] = m
                self.cell_array[k] = c
                self._input = m

                if self._dropout is not None and self._dropout > 0.0:
C
ccrrong 已提交
125
                    self._input = paddle.nn.functional.dropout(
126
                        self._input,
C
ccrrong 已提交
127 128
                        p=self._dropout,
                        mode='upscale_in_train',
129
                    )
130
            res.append(
131
                paddle.reshape(self._input, shape=[1, -1, self._hidden_size])
132
            )
133
        real_res = fluid.layers.concat(res, 0)
134
        real_res = paddle.transpose(x=real_res, perm=[1, 0, 2])
135
        last_hidden = fluid.layers.concat(self.hidden_array, 1)
136
        last_hidden = paddle.reshape(
137 138
            last_hidden, shape=[-1, self._num_layers, self._hidden_size]
        )
139
        last_hidden = paddle.transpose(x=last_hidden, perm=[1, 0, 2])
140
        last_cell = fluid.layers.concat(self.cell_array, 1)
141
        last_cell = paddle.reshape(
142 143
            last_cell, shape=[-1, self._num_layers, self._hidden_size]
        )
144
        last_cell = paddle.transpose(x=last_cell, perm=[1, 0, 2])
145
        return real_res, last_hidden, last_cell
J
JiabinYang 已提交
146 147


148
class PtbModel(fluid.Layer):
149 150 151 152 153 154 155 156 157 158
    def __init__(
        self,
        hidden_size,
        vocab_size,
        num_layers=2,
        num_steps=20,
        init_scale=0.1,
        is_sparse=False,
        dropout=None,
    ):
159
        super().__init__()
J
JiabinYang 已提交
160 161 162 163 164 165
        self.hidden_size = hidden_size
        self.vocab_size = vocab_size
        self.init_scale = init_scale
        self.num_layers = num_layers
        self.num_steps = num_steps
        self.dropout = dropout
166 167 168 169 170 171 172
        self.simple_lstm_rnn = SimpleLSTMRNN(
            hidden_size,
            num_steps,
            num_layers=num_layers,
            init_scale=init_scale,
            dropout=dropout,
        )
173
        self.embedding = Embedding(
174 175 176 177
            vocab_size,
            hidden_size,
            sparse=is_sparse,
            weight_attr=fluid.ParamAttr(
J
JiabinYang 已提交
178 179
                name='embedding_para',
                initializer=fluid.initializer.UniformInitializer(
180 181 182 183
                    low=-init_scale, high=init_scale
                ),
            ),
        )
184
        self.softmax_weight = self.create_parameter(
185 186
            attr=fluid.ParamAttr(),
            shape=[self.hidden_size, self.vocab_size],
J
JiabinYang 已提交
187 188
            dtype="float32",
            default_initializer=fluid.initializer.UniformInitializer(
189 190 191
                low=-self.init_scale, high=self.init_scale
            ),
        )
192
        self.softmax_bias = self.create_parameter(
193 194
            attr=fluid.ParamAttr(),
            shape=[self.vocab_size],
J
JiabinYang 已提交
195 196
            dtype="float32",
            default_initializer=fluid.initializer.UniformInitializer(
197 198 199
                low=-self.init_scale, high=self.init_scale
            ),
        )
J
JiabinYang 已提交
200 201

    def forward(self, input, label, init_hidden, init_cell):
202
        init_h = paddle.reshape(
203 204
            init_hidden, shape=[self.num_layers, -1, self.hidden_size]
        )
J
JiabinYang 已提交
205

206
        init_c = paddle.reshape(
207 208
            init_cell, shape=[self.num_layers, -1, self.hidden_size]
        )
J
JiabinYang 已提交
209 210

        x_emb = self.embedding(input)
211
        x_emb = paddle.reshape(
212 213
            x_emb, shape=[-1, self.num_steps, self.hidden_size]
        )
J
JiabinYang 已提交
214
        if self.dropout is not None and self.dropout > 0.0:
C
ccrrong 已提交
215
            x_emb = paddle.nn.functional.dropout(
J
JiabinYang 已提交
216
                x_emb,
C
ccrrong 已提交
217 218
                p=self.drop_out,
                mode='upscale_in_train',
219
            )
220
        rnn_out, last_hidden, last_cell = self.simple_lstm_rnn(
221 222
            x_emb, init_h, init_c
        )
223
        rnn_out = paddle.reshape(
224 225
            rnn_out, shape=[-1, self.num_steps, self.hidden_size]
        )
K
kangguangli 已提交
226
        projection = paddle.matmul(rnn_out, self.softmax_weight)
227
        projection = paddle.add(projection, self.softmax_bias)
228
        projection = paddle.reshape(projection, shape=[-1, self.vocab_size])
229
        loss = paddle.nn.functional.softmax_with_cross_entropy(
230 231
            logits=projection, label=label, soft_label=False
        )
232
        loss = paddle.reshape(loss, shape=[-1, self.num_steps])
233
        loss = paddle.mean(loss, axis=[0])
234
        loss = paddle.sum(loss)
J
JiabinYang 已提交
235 236 237 238

        return loss, last_hidden, last_cell


L
lujun 已提交
239
class TestDygraphPtbRnn(unittest.TestCase):
240
    def test_ptb_rnn(self):
241 242 243 244
        for is_sparse in [True, False]:
            self.ptb_rnn_cpu_float32(is_sparse)

    def ptb_rnn_cpu_float32(self, is_sparse):
J
JiabinYang 已提交
245 246 247 248 249 250 251
        seed = 90
        hidden_size = 10
        vocab_size = 1000
        num_layers = 1
        num_steps = 3
        init_scale = 0.1
        batch_size = 4
252
        batch_num = 200
253 254
        traced_layer = None

L
lujun 已提交
255
        with fluid.dygraph.guard():
C
cnn 已提交
256
            paddle.seed(seed)
L
Leo Chen 已提交
257
            paddle.framework.random._manual_program_seed(seed)
J
JiabinYang 已提交
258
            # TODO: marsyang1993 Change seed to
259 260 261 262 263 264 265 266 267 268 269 270
            ptb_model = PtbModel(
                hidden_size=hidden_size,
                vocab_size=vocab_size,
                num_layers=num_layers,
                num_steps=num_steps,
                init_scale=init_scale,
                is_sparse=is_sparse,
            )

            sgd = SGDOptimizer(
                learning_rate=1e-3, parameter_list=ptb_model.parameters()
            )
271 272
            dy_param_updated = dict()
            dy_param_init = dict()
J
JiabinYang 已提交
273 274 275
            dy_loss = None
            last_hidden = None
            last_cell = None
276

277 278
            helper = DyGraphProgramDescTracerTestHelper(self)
            program = None
279

280
            for i in range(batch_num):
J
JiabinYang 已提交
281 282 283 284
                x_data = np.arange(12).reshape(4, 3).astype('int64')
                y_data = np.arange(1, 13).reshape(4, 3).astype('int64')
                y_data = y_data.reshape((-1, 1))
                init_hidden_data = np.zeros(
285 286 287 288 289
                    (num_layers, batch_size, hidden_size), dtype='float32'
                )
                init_cell_data = np.zeros(
                    (num_layers, batch_size, hidden_size), dtype='float32'
                )
J
JiabinYang 已提交
290 291 292 293
                x = to_variable(x_data)
                y = to_variable(y_data)
                init_hidden = to_variable(init_hidden_data)
                init_cell = to_variable(init_cell_data)
294

295
                outs = ptb_model(x, y, init_hidden, init_cell)
296 297 298

                dy_loss, last_hidden, last_cell = outs

J
JiabinYang 已提交
299
                if i == 0:
300
                    for param in ptb_model.parameters():
301
                        dy_param_init[param.name] = param.numpy()
L
lujun 已提交
302
                dy_loss.backward()
J
JiabinYang 已提交
303
                sgd.minimize(dy_loss)
304 305 306
                ptb_model.clear_gradients()
                if i == batch_num - 1:
                    for param in ptb_model.parameters():
307
                        dy_param_updated[param.name] = param.numpy()
308

309 310 311 312
            dy_loss_value = dy_loss.numpy()
            dy_last_cell_value = last_cell.numpy()
            dy_last_hidden_value = last_hidden.numpy()

313
        with new_program_scope():
C
cnn 已提交
314
            paddle.seed(seed)
L
Leo Chen 已提交
315
            paddle.framework.random._manual_program_seed(seed)
316 317 318 319 320 321 322 323 324 325 326 327 328 329
            ptb_model = PtbModel(
                hidden_size=hidden_size,
                vocab_size=vocab_size,
                num_layers=num_layers,
                num_steps=num_steps,
                init_scale=init_scale,
                is_sparse=is_sparse,
            )

            exe = fluid.Executor(
                fluid.CPUPlace()
                if not core.is_compiled_with_cuda()
                else fluid.CUDAPlace(0)
            )
330
            sgd = SGDOptimizer(learning_rate=1e-3)
G
GGBond8488 已提交
331
            x = paddle.static.data(
332 333
                name="x", shape=[-1, num_steps], dtype='int64'
            )
G
GGBond8488 已提交
334 335 336 337 338
            x.desc.set_need_check_feed(False)
            y = paddle.static.data(name="y", shape=[-1, 1], dtype='float32')
            y.desc.set_need_check_feed(False)
            init_hidden = paddle.static.data(
                name="init_hidden", shape=[-1, 1], dtype='float32'
339
            )
G
GGBond8488 已提交
340 341 342
            init_hidden.desc.set_need_check_feed(False)
            init_cell = paddle.static.data(
                name="init_cell", shape=[-1, 1], dtype='float32'
343
            )
G
GGBond8488 已提交
344
            init_cell.desc.set_need_check_feed(False)
345 346

            static_loss, static_last_hidden, static_last_cell = ptb_model(
347 348
                x, y, init_hidden, init_cell
            )
349 350 351 352
            sgd.minimize(static_loss)
            static_param_updated = dict()
            static_param_init = dict()
            static_param_name_list = list()
353
            for param in ptb_model.parameters():
354 355
                static_param_name_list.append(param.name)

356 357 358 359
            out = exe.run(
                framework.default_startup_program(),
                fetch_list=static_param_name_list,
            )
360 361
            for i in range(len(static_param_name_list)):
                static_param_init[static_param_name_list[i]] = out[i]
J
JiabinYang 已提交
362 363 364
            static_loss_value = None
            static_last_cell_value = None
            static_last_hidden_value = None
365
            for i in range(batch_num):
366 367 368 369 370
                x_data = np.arange(12).reshape(4, 3).astype('int64')
                y_data = np.arange(1, 13).reshape(4, 3).astype('int64')
                x_data = x_data.reshape((-1, num_steps, 1))
                y_data = y_data.reshape((-1, 1))
                init_hidden_data = np.zeros(
371 372 373 374 375
                    (num_layers, batch_size, hidden_size), dtype='float32'
                )
                init_cell_data = np.zeros(
                    (num_layers, batch_size, hidden_size), dtype='float32'
                )
376 377
                fetch_list = [static_loss, static_last_hidden, static_last_cell]
                fetch_list.extend(static_param_name_list)
378 379 380 381 382 383 384 385 386 387
                out = exe.run(
                    fluid.default_main_program(),
                    feed={
                        "x": x_data,
                        "y": y_data,
                        "init_hidden": init_hidden_data,
                        "init_cell": init_cell_data,
                    },
                    fetch_list=fetch_list,
                )
388
                static_loss_value = out[0]
389 390
                static_last_hidden_value = out[1]
                static_last_cell_value = out[2]
J
JiabinYang 已提交
391

392 393
                if i == batch_num - 1:
                    for k in range(3, len(out)):
394 395 396
                        static_param_updated[
                            static_param_name_list[k - 3]
                        ] = out[k]
397

398
        np.testing.assert_array_equal(static_loss_value, dy_loss_value)
399 400 401 402 403 404
        np.testing.assert_array_equal(
            static_last_cell_value, dy_last_cell_value
        )
        np.testing.assert_array_equal(
            static_last_hidden_value, dy_last_hidden_value
        )
405
        for key, value in static_param_init.items():
406
            np.testing.assert_array_equal(value, dy_param_init[key])
407
        for key, value in static_param_updated.items():
408
            np.testing.assert_array_equal(value, dy_param_updated[key])
J
JiabinYang 已提交
409 410 411 412


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