test_word2vec_book.py 12.5 KB
Newer Older
D
dzhwinter 已提交
1
#   Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve.
L
Liu Yiqun 已提交
2 3
#
# Licensed under the Apache License, Version 2.0 (the "License");
D
dzhwinter 已提交
4 5
# 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
import paddle
16
import paddle.fluid as fluid
Y
Yang Yu 已提交
17
import unittest
Y
Yang Yu 已提交
18
import os
19
import numpy as np
20 21
import math
import sys
22
import tempfile
Q
QI JUN 已提交
23

P
pangyoki 已提交
24 25
paddle.enable_static()

Y
Yang Yu 已提交
26

27 28 29 30 31 32 33 34 35
def get_place(target):
    if target == "cuda":
        return fluid.CUDAPlace(0)
    elif target == "xpu":
        return fluid.XPUPlace(0)
    elif target == "cpu":
        return fluid.CPUPlace()
    else:
        raise ValueError(
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
            "Target `{0}` is not on the support list: `cuda`, `xpu` and `cpu`.".format(
                target
            )
        )


def train(
    target,
    is_sparse,
    is_parallel,
    save_dirname,
    is_local=True,
    use_bf16=False,
    pure_bf16=False,
):
Y
Yang Yu 已提交
51 52 53 54 55
    PASS_NUM = 100
    EMBED_SIZE = 32
    HIDDEN_SIZE = 256
    N = 5
    BATCH_SIZE = 32
Y
Yang Yu 已提交
56 57 58
    IS_SPARSE = is_sparse

    def __network__(words):
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
        embed_first = fluid.layers.embedding(
            input=words[0],
            size=[dict_size, EMBED_SIZE],
            dtype='float32',
            is_sparse=IS_SPARSE,
            param_attr='shared_w',
        )
        embed_second = fluid.layers.embedding(
            input=words[1],
            size=[dict_size, EMBED_SIZE],
            dtype='float32',
            is_sparse=IS_SPARSE,
            param_attr='shared_w',
        )
        embed_third = fluid.layers.embedding(
            input=words[2],
            size=[dict_size, EMBED_SIZE],
            dtype='float32',
            is_sparse=IS_SPARSE,
            param_attr='shared_w',
        )
        embed_forth = fluid.layers.embedding(
            input=words[3],
            size=[dict_size, EMBED_SIZE],
            dtype='float32',
            is_sparse=IS_SPARSE,
            param_attr='shared_w',
        )
Y
Yang Yu 已提交
87 88

        concat_embed = fluid.layers.concat(
89 90 91 92 93 94 95 96
            input=[embed_first, embed_second, embed_third, embed_forth], axis=1
        )
        hidden1 = fluid.layers.fc(
            input=concat_embed, size=HIDDEN_SIZE, act='sigmoid'
        )
        predict_word = fluid.layers.fc(
            input=hidden1, size=dict_size, act='softmax'
        )
Y
Yang Yu 已提交
97
        cost = fluid.layers.cross_entropy(input=predict_word, label=words[4])
98
        avg_cost = paddle.mean(cost)
99
        return avg_cost, predict_word
Y
Yang Yu 已提交
100 101 102 103 104 105 106 107 108 109

    word_dict = paddle.dataset.imikolov.build_dict()
    dict_size = len(word_dict)

    first_word = fluid.layers.data(name='firstw', shape=[1], dtype='int64')
    second_word = fluid.layers.data(name='secondw', shape=[1], dtype='int64')
    third_word = fluid.layers.data(name='thirdw', shape=[1], dtype='int64')
    forth_word = fluid.layers.data(name='forthw', shape=[1], dtype='int64')
    next_word = fluid.layers.data(name='nextw', shape=[1], dtype='int64')

L
Liu Yiqun 已提交
110
    if not is_parallel:
111
        avg_cost, predict_word = __network__(
112 113
            [first_word, second_word, third_word, forth_word, next_word]
        )
Y
Yang Yu 已提交
114
    else:
X
Xin Pan 已提交
115
        raise NotImplementedError()
Y
Yang Yu 已提交
116

Y
Yang Yu 已提交
117
    sgd_optimizer = fluid.optimizer.SGD(learning_rate=0.001)
118
    if use_bf16:
A
arlesniak 已提交
119 120 121
        sgd_optimizer = paddle.static.amp.bf16.decorate_bf16(
            sgd_optimizer,
            amp_lists=paddle.static.amp.bf16.AutoMixedPrecisionListsBF16(
122 123
                custom_fp32_list={'softmax', 'concat'},
            ),
A
arlesniak 已提交
124
            use_bf16_guard=False,
125 126
            use_pure_bf16=pure_bf16,
        )
A
arlesniak 已提交
127

128
    sgd_optimizer.minimize(avg_cost, fluid.default_startup_program())
Y
Yang Yu 已提交
129

130 131 132
    train_reader = paddle.batch(
        paddle.dataset.imikolov.train(word_dict, N), BATCH_SIZE
    )
Y
Yang Yu 已提交
133

134
    place = get_place(target)
Y
Yang Yu 已提交
135 136 137
    exe = fluid.Executor(place)
    feeder = fluid.DataFeeder(
        feed_list=[first_word, second_word, third_word, forth_word, next_word],
138 139
        place=place,
    )
Y
Yang Yu 已提交
140

武毅 已提交
141 142
    def train_loop(main_program):
        exe.run(fluid.default_startup_program())
A
arlesniak 已提交
143 144
        if pure_bf16:
            sgd_optimizer.amp_init(exe.place)
武毅 已提交
145 146 147

        for pass_id in range(PASS_NUM):
            for data in train_reader():
148 149 150
                avg_cost_np = exe.run(
                    main_program, feed=feeder.feed(data), fetch_list=[avg_cost]
                )
武毅 已提交
151
                if avg_cost_np[0] < 5.0:
A
arlesniak 已提交
152
                    if save_dirname is not None and not pure_bf16:
153 154 155
                        fluid.io.save_inference_model(
                            save_dirname,
                            ['firstw', 'secondw', 'thirdw', 'forthw'],
156 157 158
                            [predict_word],
                            exe,
                        )
武毅 已提交
159 160 161 162 163 164 165 166 167
                    return
                if math.isnan(float(avg_cost_np[0])):
                    sys.exit("got NaN loss, training failed.")

        raise AssertionError("Cost is too large {0:2.2}".format(avg_cost_np[0]))

    if is_local:
        train_loop(fluid.default_main_program())
    else:
G
gongweibao 已提交
168 169
        port = os.getenv("PADDLE_PSERVER_PORT", "6174")
        pserver_ips = os.getenv("PADDLE_PSERVER_IPS")  # ip,ip...
武毅 已提交
170 171 172 173
        eplist = []
        for ip in pserver_ips.split(","):
            eplist.append(':'.join([ip, port]))
        pserver_endpoints = ",".join(eplist)  # ip:port,ip:port...
G
gongweibao 已提交
174
        trainers = int(os.getenv("PADDLE_TRAINERS"))
武毅 已提交
175
        current_endpoint = os.getenv("POD_IP") + ":" + port
G
gongweibao 已提交
176 177
        trainer_id = int(os.getenv("PADDLE_TRAINER_ID"))
        training_role = os.getenv("PADDLE_TRAINING_ROLE", "TRAINER")
武毅 已提交
178
        t = fluid.DistributeTranspiler()
Y
Yancey1989 已提交
179
        t.transpile(trainer_id, pservers=pserver_endpoints, trainers=trainers)
武毅 已提交
180 181
        if training_role == "PSERVER":
            pserver_prog = t.get_pserver_program(current_endpoint)
182 183 184
            pserver_startup = t.get_startup_program(
                current_endpoint, pserver_prog
            )
武毅 已提交
185 186 187 188
            exe.run(pserver_startup)
            exe.run(pserver_prog)
        elif training_role == "TRAINER":
            train_loop(t.get_trainer_program())
Y
Yang Yu 已提交
189 190


191
def infer(target, save_dirname=None):
L
Liu Yiqun 已提交
192 193 194
    if save_dirname is None:
        return

195
    place = get_place(target)
L
Liu Yiqun 已提交
196
    exe = fluid.Executor(place)
197 198 199
    inference_scope = fluid.core.Scope()
    with fluid.scope_guard(inference_scope):
        # Use fluid.io.load_inference_model to obtain the inference program desc,
T
tianshuo78520a 已提交
200
        # the feed_target_names (the names of variables that will be fed
201 202
        # data using feed operators), and the fetch_targets (variables that
        # we want to obtain data from using fetch operators).
203 204 205 206 207
        [
            inference_program,
            feed_target_names,
            fetch_targets,
        ] = fluid.io.load_inference_model(save_dirname, exe)
208 209 210 211

        word_dict = paddle.dataset.imikolov.build_dict()
        dict_size = len(word_dict)

212 213 214 215 216
        # Setup inputs by creating 4 LoDTensors representing 4 words. Here each word
        # is simply an index to look up for the corresponding word vector and hence
        # the shape of word (base_shape) should be [1]. The recursive_sequence_lengths,
        # which is length-based level of detail (lod) of each LoDTensor, should be [[1]]
        # meaning there is only one level of detail and there is only one sequence of
217 218 219
        # one word on this level.
        # Note that recursive_sequence_lengths should be a list of lists.
        recursive_seq_lens = [[1]]
220
        base_shape = [1]
K
Kexin Zhao 已提交
221
        # The range of random integers is [low, high]
222 223 224 225 226 227 228 229 230 231 232 233
        first_word = fluid.create_random_int_lodtensor(
            recursive_seq_lens, base_shape, place, low=0, high=dict_size - 1
        )
        second_word = fluid.create_random_int_lodtensor(
            recursive_seq_lens, base_shape, place, low=0, high=dict_size - 1
        )
        third_word = fluid.create_random_int_lodtensor(
            recursive_seq_lens, base_shape, place, low=0, high=dict_size - 1
        )
        fourth_word = fluid.create_random_int_lodtensor(
            recursive_seq_lens, base_shape, place, low=0, high=dict_size - 1
        )
234 235 236 237 238 239 240 241

        assert feed_target_names[0] == 'firstw'
        assert feed_target_names[1] == 'secondw'
        assert feed_target_names[2] == 'thirdw'
        assert feed_target_names[3] == 'forthw'

        # Construct feed as a dictionary of {feed_target_name: feed_target_data}
        # and results will contain a list of data corresponding to fetch_targets.
242 243 244 245 246 247 248 249 250 251 252
        results = exe.run(
            inference_program,
            feed={
                feed_target_names[0]: first_word,
                feed_target_names[1]: second_word,
                feed_target_names[2]: third_word,
                feed_target_names[3]: fourth_word,
            },
            fetch_list=fetch_targets,
            return_numpy=False,
        )
F
flame 已提交
253 254 255 256 257 258 259 260 261 262 263 264 265

        def to_infer_tensor(lod_tensor):
            infer_tensor = fluid.core.PaddleTensor()
            infer_tensor.lod = lod_tensor.lod()
            infer_tensor.data = fluid.core.PaddleBuf(np.array(lod_tensor))
            infer_tensor.shape = lod_tensor.shape()
            infer_tensor.dtype = fluid.core.PaddleDType.INT64
            return infer_tensor

        infer_inputs = [first_word, second_word, third_word, fourth_word]
        infer_inputs = [to_infer_tensor(t) for t in infer_inputs]

        infer_config = fluid.core.NativeConfig()
266
        infer_config.model_dir = save_dirname
267 268
        if target == "cuda":
            infer_config.use_gpu = True
F
flame 已提交
269 270
            infer_config.device = 0
            infer_config.fraction_of_gpu_memory = 0.15
271 272
        elif target == "xpu":
            infer_config.use_xpu = True
F
flame 已提交
273
        compiled_program = fluid.compiler.CompiledProgram(inference_program)
F
flame 已提交
274
        compiled_program._with_inference_optimize(infer_config)
F
flame 已提交
275 276
        assert compiled_program._is_inference is True
        infer_outputs = exe.run(compiled_program, feed=infer_inputs)
277
        np_data = np.array(results[0])
F
flame 已提交
278 279
        infer_out = infer_outputs[0].data.float_data()
        for a, b in zip(np_data[0], infer_out):
280
            assert np.isclose(a, b, rtol=5e-5), "a: {}, b: {}".format(a, b)
L
Liu Yiqun 已提交
281 282


A
arlesniak 已提交
283
def main(target, is_sparse, is_parallel, use_bf16, pure_bf16):
284 285 286
    if target == "cuda" and not fluid.core.is_compiled_with_cuda():
        return
    if target == "xpu" and not fluid.core.is_compiled_with_xpu():
287
        return
L
Liu Yiqun 已提交
288

289 290 291
    if use_bf16 and not fluid.core.is_compiled_with_mkldnn():
        return

292
    temp_dir = tempfile.TemporaryDirectory()
L
Liu Yiqun 已提交
293
    if not is_parallel:
294
        save_dirname = os.path.join(temp_dir.name, "word2vec.inference.model")
L
Liu Yiqun 已提交
295 296 297
    else:
        save_dirname = None

298 299 300 301 302
    if target == "xpu":
        # This model cannot be trained with xpu temporarily,
        # so only inference is turned on.
        train("cpu", is_sparse, is_parallel, save_dirname)
    else:
303 304 305 306 307 308 309 310
        train(
            target,
            is_sparse,
            is_parallel,
            save_dirname,
            use_bf16=use_bf16,
            pure_bf16=pure_bf16,
        )
311
    infer(target, save_dirname)
312
    temp_dir.cleanup()
313 314


315 316 317 318 319 320 321 322
FULL_TEST = os.getenv('FULL_TEST', '0').lower() in [
    'true',
    '1',
    't',
    'y',
    'yes',
    'on',
]
Y
Yang Yu 已提交
323
SKIP_REASON = "Only run minimum number of tests in CI server, to make CI faster"
Y
Yang Yu 已提交
324 325 326


class W2VTest(unittest.TestCase):
Y
Yang Yu 已提交
327 328 329
    pass


330 331 332
def inject_test_method(
    target, is_sparse, is_parallel, use_bf16=False, pure_bf16=False
):
333
    fn_name = "test_{0}_{1}_{2}{3}".format(
334 335
        target,
        "sparse" if is_sparse else "dense",
336
        "parallel" if is_parallel else "normal",
337 338
        "_purebf16" if pure_bf16 else "_bf16" if use_bf16 else "",
    )
Y
Yang Yu 已提交
339 340 341 342 343 344 345

    def __impl__(*args, **kwargs):
        prog = fluid.Program()
        startup_prog = fluid.Program()
        scope = fluid.core.Scope()
        with fluid.scope_guard(scope):
            with fluid.program_guard(prog, startup_prog):
A
arlesniak 已提交
346
                main(target, is_sparse, is_parallel, use_bf16, pure_bf16)
Y
Yang Yu 已提交
347

348 349 350
    if (
        not fluid.core.is_compiled_with_cuda() or target == "cuda"
    ) and is_sparse:
Y
Yang Yu 已提交
351 352 353
        fn = __impl__
    else:
        # skip the other test when on CI server
354 355 356
        fn = unittest.skipUnless(condition=FULL_TEST, reason=SKIP_REASON)(
            __impl__
        )
Y
Yang Yu 已提交
357 358

    setattr(W2VTest, fn_name, fn)
Y
Yang Yu 已提交
359 360


361
for target in ("cuda", "cpu", "xpu"):
Y
Yang Yu 已提交
362
    for is_sparse in (False, True):
363
        for is_parallel in (False,):
364
            inject_test_method(target, is_sparse, is_parallel)
A
arlesniak 已提交
365 366
inject_test_method("cpu", False, False, True)
inject_test_method("cpu", False, False, True, True)
Y
Yang Yu 已提交
367 368 369

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